/*************************************************************************
**************************************************************************
**   M E G A S Q U I R T  II - 2 0 0 4 - V1.000
**
**   (C) 2003 - B. A. Bowling And A. C. Grippo
**
**   This header must appear on all derivatives of this code.
**
***************************************************************************
**************************************************************************/

/*************************************************************************
**************************************************************************
**   GCC Port
**
**   (C) 2004,2005 - Philip L Johnson
**
**   This header must appear on all derivatives of this code.
**
***************************************************************************
**************************************************************************/

/*------------------------
  Version 1.000
    This version has all the basic capabilities of MS, but much higher precision -
  in the usec range. It adds larger tuning tables (12x12), WBO2, IAC, dual tables, 
  and ignition control which assumes 1 coil and a mechanical distributor to 
  distribute spark or EDIS for multicoil wasted spark.
  Version 1.100
    Made serial comms more robust: added one word read, write + verify commands, 
    write to ram inhibit when overrun detection, receiver timeout check.
  Version 1.200
    Fixed various problems with trigger offsets ATDC (charge time can occur after
    input capture at very low rpms and other related ignition timing problems.)
  Version 1.300
    Changed tps, tpsdot units to % x 10; added accel tailoff algorithm.
  Version 1.310
    Added IAC scale factor to extend range of IAC steps.
  Version 1.340
    Fixed bug in masking of TimerIn interrupts for dt/2.
 -----------------------*/
#include "hcs12def.h"      /* common defines and macros */
#include "flash.h"         /* flashburner defines, structures */
#include <string.h>

#ifdef GCC_BUILD
#define EEPROM_ATTR __attribute__ ((section (".eeprom")))
#define INTERRUPT
#define ENABLE_INTERRUPTS __asm__ __volatile__ ("cli");
#define DISABLE_INTERRUPTS __asm__ __volatile__ ("sei");
void ISR_Ign_TimerIn(void) __attribute__((interrupt));
void ISR_Ign_TimerOut(void) __attribute__((interrupt));
void ISR_Inj1_TimerOut(void) __attribute__((interrupt));
void ISR_Inj2_TimerOut(void) __attribute__((interrupt));
void ISR_TimerOverflow(void) __attribute__((interrupt));
void ISR_Timer_Clock(void) __attribute__((interrupt));
void ISR_SCI_Comm(void) __attribute__((interrupt));
#else
#define EEPROM_ATTR
#define INTERRUPT interrupt
#define ENABLE_INTERRUPTS asm cli;
#define DISABLE_INTERRUPTS asm sei;
#pragma CODE_SEG NON_BANKED /* Interrupt section for this module. 
                          Placement will be in NON_BANKED area. */

extern void near _Startup(void);       /* Startup routine */
interrupt void UnimplementedISR(void);
interrupt void ISR_Ign_TimerIn(void);
interrupt void ISR_Ign_TimerOut(void);
interrupt void ISR_Inj1_TimerOut(void);
interrupt void ISR_Inj2_TimerOut(void);
interrupt void ISR_TimerOverflow(void);
interrupt void ISR_Timer_Clock(void);
interrupt void ISR_SCI_Comm(void);

typedef void (*near tIsrFunc)(void);
const tIsrFunc _vect[] @0xFF80 = {     /* Interrupt table */
        UnimplementedISR,                 /* vector 63 */
        UnimplementedISR,                 /* vector 62 */
        UnimplementedISR,                 /* vector 61 */
        UnimplementedISR,                 /* vector 60 */
        UnimplementedISR,                 /* vector 59 */
        UnimplementedISR,                 /* vector 58 */
        UnimplementedISR,                 /* vector 57 */
        UnimplementedISR,                 /* vector 56 */
        UnimplementedISR,                 /* vector 55 */
        UnimplementedISR,                 /* vector 54 */
        UnimplementedISR,                 /* vector 53 */
        UnimplementedISR,                 /* vector 52 */
        UnimplementedISR,                 /* vector 51 */
        UnimplementedISR,                 /* vector 50 */
        UnimplementedISR,                 /* vector 49 */
        UnimplementedISR,                 /* vector 48 */
        UnimplementedISR,                 /* vector 47 */
        UnimplementedISR,                 /* vector 46 */
        UnimplementedISR,                 /* vector 45 */
        UnimplementedISR,                 /* vector 44 */
        UnimplementedISR,                 /* vector 43 */
        UnimplementedISR,                 /* vector 42 */
        UnimplementedISR,                 /* vector 41 */
        UnimplementedISR,                 /* vector 40 */
        UnimplementedISR,                 /* vector 39 */
        UnimplementedISR,                 /* vector 38 */
        UnimplementedISR,                 /* vector 37 */
        UnimplementedISR,                 /* vector 36 */
        UnimplementedISR,                 /* vector 35 */
        UnimplementedISR,                 /* vector 34 */
        UnimplementedISR,                 /* vector 33 */
        UnimplementedISR,                 /* vector 32 */
        UnimplementedISR,                 /* vector 31 */
        UnimplementedISR,                 /* vector 30 */
        UnimplementedISR,                 /* vector 29 */
        UnimplementedISR,                 /* vector 28 */
        UnimplementedISR,                 /* vector 27 */
        UnimplementedISR,                 /* vector 26 */
        UnimplementedISR,                 /* vector 25 */
        UnimplementedISR,                 /* vector 24 */
        UnimplementedISR,                 /* vector 23 */
        UnimplementedISR,                 /* vector 22 */
        UnimplementedISR,                 /* vector 21 */
        ISR_SCI_Comm,                     /* vector 20 */
        UnimplementedISR,                 /* vector 19 */
        UnimplementedISR,                 /* vector 18 */
        UnimplementedISR,                 /* vector 17 */
        ISR_TimerOverflow,                /* vector 16 */
        UnimplementedISR,                 /* vector 15 */
        UnimplementedISR,                 /* vector 14 */
        ISR_Ign_TimerOut,                 /* vector 13 */
        UnimplementedISR,                 /* vector 12 */
        ISR_Inj2_TimerOut,                /* vector 11 */
        UnimplementedISR,                 /* vector 10 */
        ISR_Inj1_TimerOut,                /* vector 09 */
        ISR_Ign_TimerIn,                  /* vector 08 */
        ISR_Timer_Clock,                  /* vector 07 */
        UnimplementedISR,                 /* vector 06 */
        UnimplementedISR,                 /* vector 05 */
        UnimplementedISR,                 /* vector 04 */
        UnimplementedISR,                 /* vector 03 */
        UnimplementedISR,                 /* vector 02 */
        UnimplementedISR,                 /* vector 01 */
        _Startup                          /* Reset vector */
   };
#pragma CODE_SEG DEFAULT
   
#endif

#include "cltfactor.inc"
#include "matfactor.inc"
#include "egofactor.inc"

#define NO_TBLES    3
#define NO_FMAPS 	12
#define NO_SMAPS 	12
#define NO_FRPMS 	12
#define NO_SRPMS 	12
#define NO_INJ		2
#define NO_TEMPS 	10
#define NO_TPS_DOTS   4
#define NO_MAP_DOTS   4
#define CRANK_RPM   300
#define NPORT    2
#define NO_COILCHG_PTS  5

// User inputs - 1 set in flash, 1 in ram
typedef struct {
unsigned char no_cyl,no_skip_pulses,      // skip >= n pulses at startup
    ICIgnOption,     // Bit 0: Input capture: 0 = falling edge, 1 rising
                     // Bit 1: 1= trigger return mode in cranking; for this
          // mode, set bit 0 to normal edge, then opp.edge used in cranking.
          // trigger return not applicable to EDIS
                     // Bits 2-3: spare
	                   // Bits 4-7: 0 = standard - charge coil, spark
	                   //           1 = Ford  EDIS option
	                   //           2 = EDIS with multispark
    spkout_hi_lo,    // Ign Output compare: 0 = spark low (gnd)
    max_coil_dur,max_spk_dur,DurAcc;        // ms x 10
char deltV_table[NO_COILCHG_PTS],deltDur_table[NO_COILCHG_PTS], // Vx10,msx10
    RevLimOption,     // 0=none, 1=spark retard, 2=fuel cut
    RevLimMaxRtd,     // max amount of spark retard (degx10) (at Rpm2 below)            
    PredOpt;          // Option for prediction algorithm for next tach plse
                      //  0=use last time interval
                      //  1=use 1st deriv prediction; 2=use 1st deriv at hi
                      //   rpm, 2nd deriv prediction at lo rpm;
                      //  3=use 2nd deriv prediction always.
int adv_table[NO_SMAPS][NO_SRPMS],cold_adv_table[NO_TEMPS],
   adv_offset,        // all in deg x 10
   //     adv_offset is no deg(x10) between trigger (Input Capture) and TDC.
   //     It may be +btdc (IC1 to TDC) or -atdc (TDC to IC2).
   //
   //    |               |     |
   //    |               |     |
   //   -|---------------|-----|-
   //   IC1             TDC    IC2
   RevLimRpm1,RevLimRpm2;  // Optn 1:Retard spk by 0 deg at Rpm1 and increase
                           //    to RevLimMaxRtd at Rpm2
                        // Optn 2:Cut fuel above Rpm2, restore below Rpm1
unsigned char ve_table[NO_INJ][NO_FMAPS][NO_FRPMS],  // %
   afr_table[NO_INJ][NO_FMAPS][NO_FRPMS],            // afr x 10
   warmen_table[NO_TEMPS],iacstep_table[NO_TEMPS], // % enrichment vs temp
                                                   // iac steps
   tpsen_table[NO_TPS_DOTS], // accel enrichment pw in .1 ms units vs tpsdot
   mapen_table[NO_MAP_DOTS]; // accel enrichment pw in .1 ms units vs mapdot
unsigned int frpm_table[NO_FRPMS],srpm_table[NO_SRPMS];
int fmap_table[NO_FMAPS],smap_table[NO_SMAPS],	    // kpa x 10, 
   temp_table[NO_TEMPS],             // deg x 10 (C or F)
   tpsdot_table[NO_TPS_DOTS],        // change in % x 10 per .1 sec
   mapdot_table[NO_MAP_DOTS];        // change in kPa x 10 per .1 sec
int map0,mapmax,clt0,cltmult,mat0,matmult,tps0,tpsmax,batt0,battmax,
                  // kPa x 10, deg F or C x 10, adc counts, volts x 10
   ego0,egomult,baro0,baromax,bcor0,bcormult,knock0,knockmax;
                  // afr x 10, kpa x 10, volts x 10
int Dtpred_Gain;  // %
unsigned char PulseTol,   // % tolerance for next input pulse    
 IdleCtl,         // idle: 0 = none, 1= solenoid, 2= iac stepper motor
                  //  - enabled only when moving, 3 = iac motor - always enabled.
                   // 4 = Ford pwm.
 IACtstep,        // iac stepper motor nominal time between steps (.1 ms units)
 IACaccstep,      // iac stepper motor accel/decel step time (.1 ms)-future use
 IACnaccstep,     // iac stepper motor no. of accel/decel steps - future use
 IACStart;        // no. of steps to send at startup to put stepper 
                  //    motor at reference (wide open) position
int IdleHyst;     // amount clt temp must move before Idle position is changed
unsigned char CWU,CWH,  // crank pulsewidths at min, max temps in temp_table (ms x 10)
 AWEV,            // after start warmup % enrich add-on value,
 AWC,             // after start enrichment no. of cycles
 Tpsacold,        // cold (-40F) accel amount in .1 ms units
 AccMult,         // cold (-40F) accel multiply factor (%)
 TpsThresh,       // tpsdot threshhold for acc/decel enrichment(change in %x10 per .1 sec) 
 MapThresh,       // mapdot threshhold for acc/decel enrichment(change in kPax10 per .1 s) 
 TpsAsync,        // clock duration (in .1 sec tics) for accel enrichment
 TPSDQ;           // deceleration fuel cut option (%)
int TPSWOT,       // TPS value at WOT (for flood clear) in %x10
 TPSOXLimit;      // Max tps value (%x10) where O2 closed loop active
unsigned char Tps_acc_wght,    // weight (0-100) to be given to tpsdot for accel enrichment.
                  //  100 - Tps_acc_wght will then be given to mapdot.
 BaroOption,      // 0=no baro, 1=baro is 1st reading of map (before cranking),
                  // 2=independent barometer (=> EgoOption < 3)
 EgoOption,       // 0 = no ego;1= nb o2;2=single wbo2;3=dual wbo2. NOTE:
                  //  BaroOption MUST be < 2 if EgoOption = 3
 EgoCountCmp,     // Ign Pulse counts between when EGO corrections are made
 EgoStep,         // % step change for EGO corrections
 EgoLimit,        // Upper/Lower rail limit (egocorr inside 100 +/- limit)
 AFRTarget,       // NBO2 AFR determining rich/ lean
 Temp_Units;      // 0= coolant & mat in deg F; 1= deg C
int FastIdle,     // fast idle Off temperature (idle_ctl = 1 only)
 EgoTemp,         // min clt temp where ego active
 RPMOXLimit;      // Min rpm where O2 closed loop is active
unsigned int ReqFuel;     // fuel pulsewidth (usec) at wide open throttle
unsigned char Divider,    // divide factor for input tach pulses
 Alternate,       // option to alternate injector banks
 InjOpen,         // Injector open time (.1 ms units)
 InjPWMTim,       // Time (.1 ms units) after Inj opening to start pwm
 InjPWMPd,        // Inj PWM period (us) - keep between 10-25 KHz (100-40 us)
 InjPWMDty,       // Inj PWM duty cycle (%)
 BatFac,          // Battery fuel pw correction factor (msx10)
 EngStroke,       // 0 = 4 stroke
                  // 1 = 2 stroke     
 InjType,         // 0 = port injection
                  // 1 = throttle body
 NoInj,           // no. of injectors (1-12)
 OddFire1,        // > 0 = odd fire option - smaller angle bet firings (deg)
 OddFire2,        // > 0 = odd fire option - larger angle bet firings (deg)
 PrimeP,          // priming pulsewidth (.1 ms units)
 MapAveno,RpmAveno,   // no. of points (Max of 8) for map, rpm rolling averages
 dual_tble_optn,  // 1 = use dual table option - ve, afr tables for each inj
 FuelAlpha,       // option for alpha-N mode: 0=none;1=use map,tps blend algorithm;
                  //   2=same as 1, but don't use kpa multiplier in plsewidth eq.
 IgnAlpha,        // option to use map,tps(alphaN) blend algorithm for ignition
 AfrAlpha,        // option to use map,tps(alphaN) blend algorithm for WBO2 AFR
 cpad1;           // byte pad
int alpha_lorpm,  // option to use map,tps(alphaN) blend algorithm for fuel
 alpha_hirpm,     //  use tps if < lorpm; use map if > hirpm; else blend., in which
                  //  case lo, hirpm should be consecutive entries in frpm_table.
                  //    set lorpm=hirpm =0 to use map enrichment only,
                  //    set lorpm =25K to use alpha-N enrichment only,
 alpha_map_table[6][6], // Table of ave map values (kpa x 10) for [tps][rpm]
                  //    pairs. Used only for alpha-N algorithm.
                  //  Allows ve table to retain same ve values
                  //  whether in alpha-N mode or not,
 amap_tps[6];     // Tps values (% x 10) for alpha_map table
unsigned int amap_rpm[6];     // Rpm values for alpha_map table
unsigned long baud;         // baud rate
int MAPOXLimit;   // Max map value (kPax10) where O2 closed loop active
// generic spare port parameters: spr_port = 0(don't use)/1(use),where
// spr_port[0] is PM2,spr_port[1] is PE4; they are set as outs to main pcb;
// out_offset= word(int) offset from start of outpc structure and
// indicates which int variable to be tested for setting port (can't
// use byte variables); condition='<', '>', '='; port_val= value(0/1)
// to which the port will be set when the condition is met:
// <out_offset word>  <condition>  <thresh +/- hyst(eresis)>.
// Hysteresis works as ff: if outpc word > thresh, set port to val and 
// leave until out word < thresh - hyst, then set to 1 - val; 
// similar for <; if outpc word between thresh +/- hyst, set port = val,
// else set to 1 - val. Note: the '=' condition can result in PWM near
// the edges of the band - this is good for warning lights, but may not
// be good for other applications. 
char spr_port[NPORT],out_offset[NPORT],condition[NPORT],port_val[NPORT];
int thresh[NPORT],hyst[NPORT];
unsigned char TpsAsync2, cpad2;  // accel tailoff duration (sec x 10)
int TpsAccel2;			// end pulsewidth of accel enrichment (ms x 10)
int IACScale;     // scale factor which multiplies IACStart and iacstep_table
int inpt_spare[6];  // spares
} inputs;

#ifndef GCC_BUILD
#pragma ROM_VAR INP_ROM
#endif
// flash copy of inputs - initialized
const inputs inpflash EEPROM_ATTR = {
8,               // no_cyl (1-12)
3,               // no_skip_pulses,  skip >=3 pulses
0x00, 0,         // ICIgnOption,spkout_hi_lo,
31, 20, 6,       // max_coil_dur,max_spk_dur,DurAcc   msx10
{-40,-20,0,20,40},   // deltaV_table[], Vx10 = batt_voltx10 - 120
{24,  9, 0,-5,-9},   // deltaDur_table[], msx10 = correction for batt_volt
1,           // RevLimOption:0,none; 1,retard spk; 2,fuel cut
120,         // RevLimMaxRtd,   deg x 10
2,           // PredOpt	 (For EDIS PredOpt=0 is sufficient)
             // For EDIS keep total adv (incl. offset & cold adv) < 60 deg
{{120,             // adv_table[MAP/tps no=0][RPM no=0], deg x 10
    120,128,145,190,347,360,360,360,360,360,360},
{120,             // adv_table[MAP/tps no=1][RPM no=0], deg x 10
    136,141,152,190,342,360,360,360,360,360,360},
{120,             // adv_table[MAP/tps no=2][RPM no=0], deg x 10
    140,148,172,197,337,360,360,360,360,360,360},
{120,             // adv_table[MAP/tps no=3][RPM no=0], deg x 10
    160,180,200,218,317,340,360,360,360,360,360},
{130,             // adv_table[MAP/tps no=4][RPM no=0], deg x 10
    180,200,220,240,309,325,360,360,360,360,360},
{140,             // adv_table[MAP/tps no=5][RPM no=0], deg x 10
    170,190,216,260,302,310,350,360,360,360,360},
{150,             // adv_table[MAP/tps no=6][RPM no=0], deg x 10
    160,180,202,240,275,295,335,360,360,360,360},
{150,             // adv_table[MAP/tps no=7][RPM no=0], deg x 10
    150,170,190,220,250,280,320,360,360,360,360},
{140,             // adv_table[MAP/tps no=8][RPM no=0], deg x 10
    140,160,180,200,225,265,305,360,360,360,360},
{120,             // adv_table[MAP/tps no=9][RPM no=0], deg x 10
    130,150,170,180,210,250,293,360,360,360,360},
{120,             // adv_table[MAP/tps no=10][RPM no=0], deg x 10
    130,140,160,170,195,235,284,360,360,360,360},
{120,             // adv_table[MAP/tps no=11][RPM no=0], deg x 10
    130,140,150,160,180,220,275,360,360,360,360}},
{60,              // cold_adv_table[TEMP no = 0], deg x 10
   50,40,30,20,10,0,0,0,0},
0,                // adv_offset,   deg x 10
5500, 6000,       // RevLimRpm1,2
{{{58,            // ve_table[inj1][MAP/tps no =0][RPM no = 0]
   58,59,63,66,68,69,69,69,67,65,63},
{58,              // ve_table[inj1][MAP/tps no =1][RPM no = 0]
   59,63,67,70,73,74,74,73,72,69,67},
{58,              // ve_table[inj1][MAP/tps no =2][RPM no = 0]
   61,65,69,72,75,76,76,76,74,71,69},
{58,              // ve_table[inj1][MAP/tps no =3][RPM no = 0]
   62,67,71,75,77,78,79,78,76,74,71},
{60,              // ve_table[inj1][MAP/tps no =4][RPM no = 0]
   66,71,76,79,82,83,83,82,81,78,75},
{61,              // ve_table[inj1][MAP/tps no =5][RPM no = 0]
   68,73,78,81,84,85,86,85,83,80,78},
{63,              // ve_table[inj1][MAP/tps no =6][RPM no = 0]
   70,75,80,84,86,88,88,87,85,82,80},
{66,              // ve_table[inj1][MAP/tps no =7][RPM no = 0]
   73,79,84,88,91,92,93,92,90,87,84},
{68,              // ve_table[inj1][MAP/tps no =8][RPM no = 0]
   75,81,86,90,93,95,95,94,92,89,86},
{71,              // ve_table[inj1][MAP/tps no =9][RPM no = 0]
   79,85,91,95,98,99,100,99,96,93,90},
{73,              // ve_table[inj1][MAP/tps no =10][RPM no = 0]
   81,87,93,97,100,102,102,101,99,95,92},
{75,              // ve_table[inj1][MAP/tps no =11][RPM no = 0]
   83,89,95,99,103,104,104,103,101,98,94}},
{{58,            // ve_table[inj2][MAP/tps no =0][RPM no = 0]
   58,59,63,66,68,69,69,69,67,65,63},
{58,              // ve_table[inj2][MAP/tps no =1][RPM no = 0]
   59,63,67,70,73,74,74,73,72,69,67},
{58,              // ve_table[inj2][MAP/tps no =2][RPM no = 0]
   61,65,69,72,75,76,76,76,74,71,69},
{58,              // ve_table[inj2][MAP/tps no =3][RPM no = 0]
   62,67,71,75,77,78,79,78,76,74,71},
{60,              // ve_table[inj2][MAP/tps no =4][RPM no = 0]
   66,71,76,79,82,83,83,82,81,78,75},
{61,              // ve_table[inj2][MAP/tps no =5][RPM no = 0]
   68,73,78,81,84,85,86,85,83,80,78},
{63,              // ve_table[inj2][MAP/tps no =6][RPM no = 0]
   70,75,80,84,86,88,88,87,85,82,80},
{66,              // ve_table[inj2][MAP/tps no =7][RPM no = 0]
   73,79,84,88,91,92,93,92,90,87,84},
{68,              // ve_table[inj2][MAP/tps no =8][RPM no = 0]
   75,81,86,90,93,95,95,94,92,89,86},
{71,              // ve_table[inj2][MAP/tps no =9][RPM no = 0]
   79,85,91,95,98,99,100,99,96,93,90},
{73,              // ve_table[inj2][MAP/tps no =10][RPM no = 0]
   81,87,93,97,100,102,102,101,99,95,92},
{75,              // ve_table[inj2][MAP/tps no =11][RPM no = 0]
   83,89,95,99,103,104,104,103,101,98,94}}},
{{{160,             // afr_table[inj1][MAP/tps no =0][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =1][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =2][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =3][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =4][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =5][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =6][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =7][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =8][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =9][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =10][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj1][MAP/tps no =11][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120}},
{{160,             // afr_table[inj2][MAP/tps no =0][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =1][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =2][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =3][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =4][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =5][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =6][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =7][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =8][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =9][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =10][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120},
{160,             // afr_table[inj2][MAP/tps no =11][RPM no = 0], afrx10
    160,150,147,147,147,140,130,120,120,120,120}}},
{180,             // warmen_table[TEMP no = 0],   % enrichment vs temp
    180,160,150,135,125,113,108,102,100},
{40,              // iacstep_table[TEMP no = 0],
  60,75,90,105,120,130,140,150,160}, 
{20,              // tpsen_table[TPS_DOT no = 0], enrichment in .1ms vs tpsdot
  50,105,150}, 
{0,               // mapen_table[TPS_DOT no = 0], enrichment in .1ms vs mapdot
  0,0,0}, 
{500,             // frpm_table[RPM no = 0] , use in VE, AFR tables
  1000,1500,2000,2800,3600,4400,5200,5200,5200,5200,5200},
{500,             // srpm_table[RPM no = 0] , use in spark advance table
  1000,1500,2000,2800,3600,4400,5200,5200,5200,5200,5200},
{200,             // fmap_table[MAP/tps no = 0], kPa x 10 , use for VE, AFR
  300,400,500,600,750,900,1000,1000,1000,1000,1000},
{200,             // smap_table[MAP/tps no = 0], kPa x 10 , use for spk adv
  300,400,500,600,750,900,1000,1000,1000,1000,1000},
{-400,            // temp_table[TEMP no = 0],  deg x 10
     -200,0,200,400,600,800,1000,1300,1600},
{100,              // tpsdot_table[TPS_DOT no =0],
  400,800,1540},   //    change in % x 10 per .1 sec
{0,               // mapdot_table[TPS_DOT no =0],
  0,0,0},         //    change in kPa x 10 per .1 sec
93,              // map0,         kPa x 10, value @ 0 ADC counts
2609,            // mapmax,       kPa x 10, value @ max(1023) ADC counts
0,               // clt0,         deg (C or F) x 10
100,             // cltmult,      %
0,               // mat0,         deg (C or F) x 10
100,             // matmult,      %
0,               // tps0,         adc counts
1023,            // tpsmax,       adc counts
1,               // batt0,        v x 10
297,             // battmax,      v x 10
0,               // ego0,         afr x 10
100,             // egomult,      %
93,              // baro0,        kPa x 10
2609,            // baromax,      kPa x 10
147, -47,        // bcor0,bcormult  kpax10, slope
0,               // knock0,       v x 10
50,              // knockmax,     v x 10
20,              // Dtpred_Gain,  %
50,              // PulseTol,     % tolerance for next input pulse    
2,               // IdleCtl, idle: 0 = none, 1= solenoid, 2= iac stepper motor
                 //  - enabled only when moving, 3 = iac motor - always enabled.
                 // 4 = Ford PWM iac
25,              // IACtstep,  .1 ms units (25 gives pulse freq of 400 Hz)
0,               // IACaccstep
0,               // IACnaccstep
160,             // IACStart,  no. of steps to send at startup to put stepper 
                 //    motor at reference (wide open) position
50,              // IdleHyst amount (degx10)
120, 40,         // CWU,CWH, crank pw at min, max temps in temp_table (ms x 10)
35,              // AWEV, after start warmup % enrich add-on value,
250,             // AWC,  after start enrichment no. of cycles
90,              // Tpsacold,  cold (-40F) accel amount in .1 ms units
100,             // AccMult,   cold (-40F) accel multiply factor (%)
100,             // TpsThresh, tpsdot threshhold for accel enrichment(change in %x10 per .1 s) 
40,              // MapThresh, mapdot threshhold for accel enrichment(change in kPax10 per .1 s) 
2,               // TpsAsync,  clock duration (in .1 sec tics) for accel enrichment
90,              // TPSDQ,  deceleration fuel cut option (%)
700,             // TPSWOT, TPS value at WOT (for flood clear), %x10
700,             // TPSOXLimit,  Max tps value (%x10) where O2 closed loop active
100,             // Tps_acc_wght, weight to be given to tpsdot for accel enrichment.
                 //  100 - Tps_acc_wght will then be given to mapdot.
1,               // BaroOption,  0=no baro, 1=baro is 1st reading of map (before cranking),
                 //   2=independent barometer
1,               // EgoOption,  0 = no ego;1= nb o2;2=single wbo2;3=dual wbo2.
16,              // EgoCountCmp,  Ign Pulse counts between when EGO corrections are made
1,               // EgoStep,   % step change for EGO corrections
15,              // EgoLimit,  Upper/Lower rail limit (egocorr inside 100 +/- limit)
140,             // AFRTarget,  NBO2 afr (afrx10) determining rich/ lean
0,               // Temp_Units,    0= coolant & mat in deg F; 1= deg C
1400,            // FastIdle, fast idle temperature (degx10) (idle_ctl = 1 only)
1600,            // EgoTemp,  min clt temp where ego active, degx10
1300,            // RPMOXLimit,  Min rpm where O2 closed loop is active
15500,           // ReqFuel;  fuel pulsewidth (usec) at wide open throttle
4,               // Divider,   divide factor for input tach pulses
1,               // Alternate,   option to alternate injector banks
10,              // InjOpen,  Injector open time (.1 ms units)
255,             // InjPWMTim,   Time (.1 ms units) after opening to start pwm
66,              // InjPWMPd,    Injector PWM period (us)
75,              // InjPWMDty,   Injector PWM duty cycle (%)
12,              // BatFac,  Battery fuel pw correction factor (msx10)
0,               // EngStroke,  0 = 4 stroke,  1 = 2 stroke     
0,               // InjType,  0 = port injection,  1 = throttle body
8,               // NoInj,    no. of injectors (1-12)
0,0,             // OddFire smaller, larger angle between firings
20,              // PrimeP,   priming pulsewidth (.1 ms units)
2,2,             // MapAveno, RpmAveno   No. points (Max of 8) for averaging
0,               // dual table option
0,               // fuel alpha-N, map blend option
0,               // ign alpha-N, map blend option
0,               // WBO2 AFR alpha-N, map blend option
0,               // byte pad 
0,               // lo rpm for alpha-N, map blend option
0,               // alpha-N hi rpm
{{1000,600,300,350,400,400}, // alpha_map_table[tps no =0][RPM nos 0-5] (kpa x 10)
{1000,650,350,400,450,450}, // alpha_map_table[tps no =1][RPM nos 0-5]
{1000,700,400,450,500,500}, // alpha_map_table[tps no =2][RPM nos 0-5]
{1000,750,450,500,550,550}, // alpha_map_table[tps no =3][RPM nos 0-5]
{1000,800,500,550,600,600}, // alpha_map_table[tps no =4][RPM nos 0-5]
{1000,800,500,550,600,600}}, // alpha_map_table[tps no =5][RPM nos 0-5]
{0,120,160,300,600,600}, // amap_tps[0 - 5] (% x 10);
{0,500,1000,1500,2000,2000},  // amap_rpm[0 - 5]
115200,                   // baud rate
900,          // MAPOXLimit, Max MAP value (kPax10) where O2 closed loop active
{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},  // spare port parameters
1,0,          // accel tail duration (sec x 10)
20,           // accel end pwidth enrichment (ms x 10)
1,            // IAC Scale factor
{0,0,0,0,0,0}     // spares
};
const unsigned char inp_spare[1536 - sizeof(inpflash)] EEPROM_ATTR ={0}; 
                                // 1536 = 3 blocks flash
#ifndef GCC_BUILD
#pragma ROM_VAR DEFAULT

#pragma ROM_VAR OVF_ROM
#endif
// IAC stepper motor sequence
const unsigned char IACCoilA[8] = {0,0,1,1,0,0,1,1};
const unsigned char IACCoilB[8] = {1,0,0,1,1,0,0,1};
// array of TC overflow numbers at which to increment secs cntr.
const unsigned int TC_ovfla[60] = {
23,
46,
69,
92,
114,
137,
160,
183,
206,
229,
252,
275,
298,
320,
343,
366,
389,
412,
435,
458,
481,
504,
526,
549,
572,
595,
618,
641,
664,
687,
710,
732,
755,
778,
801,
824,
847,
870,
893,
916,
938,
961,
984,
1007,
1030,
1053,
1076,
1099,
1122,
1144,
1167,
1190,
1213,
1236,
1259,
1282,
1305,
1328,
1350,
1373
};
const char RevNum[20] =  {    // interface no.
  "MSII Rev 1.31000   "
},
 Signature[32] = {            // code version
  "** V1.34Embedded Code by B&G **"
 };
#ifndef GCC_BUILD
#pragma ROM_VAR DEFAULT
#endif

// ram copy of inputs
inputs inpram;

// sensor variables
int last_tps,last_map,tpsdot_ltch,mapdot_ltch;
// fuel variables
unsigned int pwcalc1,pwcalc2,pw_open;
unsigned int Fl1OCt_overflow,Fl2OCt_overflow;
unsigned long Fl1TimerComp,Fl2TimerComp;
unsigned char pwm1_on,pwm2_on;
unsigned long pwm1_time,pwm2_time;
// ignition variables
unsigned char SPK,CHG, pulse_no,ign_state,ign_setpin,
  IgnOCpinstate,first_edis;
long adv_us,charge_time,coil_dur_set;
int ICt_overflow,IgnOCt_overflow,coil_dur,ic_adv_deg;
unsigned long IgnTimerComp,dtpred;
// IAC variables
unsigned long motor_time;
int motor_step, IACmotor_pos,last_iacclt;
char IAC_moving,IACmotor_reset;
// General variables
unsigned int mms,millisec,burn_flag,iacpwmctr;
unsigned int TC_ovflow,TC_ov_ix;
unsigned long lmms,t_enable_IC,t_chgoff,Rpm_Coeff,ltch_lmms,rcv_timeout;
unsigned char flocker,tpsaclk,egocount,asecount,igncount,altcount,
	first_adc,txmode,tble_type,synch,trig_ret_mode,reinit_flag;
/* Clocks:
  	- igncount: counts up each tach pulse, cleared when hit Divider pulses
               (and injection occurs).
  	- asecount: counts up each time igncount = 0 (each Divider pulses).
  	- egocount: counts up each tach pulse, cleared when hits EgoCountCmp
  	- tpsaclk: counts every .1 sec
  	- altcount: flips 0,1,0,1... on each injection, resulting in firing alternate
                injector banks if Alternate option.
*/
unsigned int txcnt,txgoal,rxoffset,rxnbytes,rxcnt,
  tble_word,ntword,no_tble_words[NO_TBLES];
unsigned int *tble_addr[NO_TBLES];
int kpa;
// Averaging variables
unsigned int rpm_buf[8];
int map_buf[8];
unsigned long rpm_bufsum,map_bufsum;
unsigned char rpm_aveix,map_aveix,rpm_avestate,map_avestate;

// rs232 Outputs to pc
typedef struct {
unsigned int seconds,pw1,pw2,rpm;           // pw in usec
int adv_deg;                                // adv in deg x 10
unsigned char squirt,engine,afrtgt1,afrtgt2;    // afrtgt in afr x 10
/* 
; Squirt Event Scheduling Variables - bit fields for "squirt" variable above
inj1:    equ    0       ; 0 = no squirt; 1 = inj squirting
inj2:    equ    1

; Engine Operating/Status variables - bit fields for "engine" variable above
ready:  equ     0       ; 0 = engine not ready; 1 = ready to run 
                                               (fuel pump on or ign plse)
crank:  equ     1       ; 0 = engine not cranking; 1 = engine cranking
startw: equ     2       ; 0 = not in startup warmup; 1 = in startw enrichment
warmup: equ     3       ; 0 = not in warmup; 1 = in warmup
tpsaen: equ     4       ; 0 = not in TPS acceleration mode; 1 = TPS acceleration mode
tpsden: equ     5       ; 0 = not in deacceleration mode; 1 = in deacceleration mode
*/

unsigned char wbo2_en1,wbo2_en2; // from wbo2 - indicates whether wb afr valid
int baro,map,mat,clt,tps,batt,ego1,ego2,knock,   // baro - kpa x 10 
                                                 // map - kpa x 10
                                                 // mat, clt deg(C/F)x 10
                                                 // tps - % x 10
                                                 // batt - vlts x 10
                                                 // ego1,2 - afr x 10
                                                 // knock - volts x 10
 egocor1,egocor2,aircor,warmcor,                 // all in %
 tpsaccel,tpsfuelcut,barocor,gammae,             // tpsaccel - acc enrich(.1 ms units)
                                                 // tpsfuelcut - %
                                                 // barcor,gammae - %
 vecurr1,vecurr2,iacstep,cold_adv_deg,           // vecurr - %
                                                 // iacstep - steps
                                                 // cold_adv_deg - deg x 10
tpsdot,mapdot,                                   // tps, map rate of change - %x10/.1 sec,
                                                 // kPax10 / .1 sec
coil_dur,                                        // msx10 coil chge set by ecu
spare1,spare2,spare3,spare4,spare5,spare6,spare7,spare8,spare9;
} variables;

variables outpc, txbuf;

// Prototypes - Note: ISRs prototyped above.
void ign_reset(void);
int move_IACmotor(void);
void fburner(unsigned int* progAdr, unsigned int* bufferPtr, 
  					 unsigned int no_words);
void tburner(char erase_write, unsigned int* addr, unsigned int word); 
int ign_table(unsigned int rpm, int map);
int cold_ign_table(int clt);
int barocor_table(int baro);
int aircor_table(int mat);
int warmcor_table(int clt);
int iaccmd_table(int clt);
int tpscor_table(int tpsdot);
int mapcor_table(int mapdot);
int vecalc_table(unsigned int rpm, int map, char inj);
int afrcalc_table(unsigned int rpm, int map, char inj);
int alphcalc_table(unsigned int rpm, int tps);
void set_spr_port(char port, char val);
int coil_dur_table(int delta_volt);
void Flash_Init(unsigned long oscclk);
void Flash_Erase_Sector(unsigned int *address);
void Flash_Write_Word(unsigned int *address, unsigned int data);

extern void DoOnStack(unsigned int* address);
extern void reboot(void);
extern void monitor(void);

int main(void) {
int ix;
int tmp1,tmp2,tmp3,tmp4,wrmtmp,tpsatmp,tpsaccel_end;
unsigned int utmp1;
long adcval,lsum,lsum1,lsum2,beta,ltmp;
int egostep;

  // initalize PLL - reset default is Oscillator clock
  // 8 MHz oscillator, PLL freq = 48 MHz, 24 MHz bus, 
  //  divide by 16 for timer of 2/3 usec tic
  PLLCTL &= 0xBF;     // Turn off PLL so can change freq
  SYNR = 0x02;        // set PLL/ Bus freq to 48/ 24 MHz
  REFDV = 0x00;
  PLLCTL |= 0x40;     // Turn on PLL
  // wait for PLL lock
  while (!(CRGFLG & 0x08));
  CLKSEL = 0x80;      // select PLL as clock
  // wait for clock transition to finish
  for (ix = 0; ix < 60; ix++);
    
  // open flash programming capability
  Flash_Init(8000);
  //inp_spare used to force inpflash into 3 sectors. It has to be
  // used in the program or it won't use up the 3rd sector. POS CW 
  // ignores the pragma making this stupid statement necessary.
  ix = inp_spare[0];

  // load all user inputs from Flash to RAM
  memcpy(&inpram,&inpflash,sizeof(inpflash));
  
  // set up i/o ports
  //    - port M0 is fuel pump relay
  //    - port M1 is fast idle solenoid
  //    - port M2 is spare (output)
  //    - port M3 is inj led
  //    - port M4 is accel led
  //    - port M5 is warmup led
  //    - port P5 is bootload pin (input)
  //    - port T5 is ignition OC
  //    - port T6 is IAC Coil A
  //    - port T7 is IAC Coil B
  //    - port B4 is IAC Enable
  DDRM = 0xFF;     // port M - all outputs, full drive by default
  DDRT |= 0xE0;    // port T5-7 - outputs
  DDRB |= 0x10;    // port B4 - output
  // turn off fuel pump, solenoids, leds
  PTM = 0x00;
  PTT &= 0x3F;      // turn off IAC coils
  if(inpram.IdleCtl == 3)  {
    PORTB &= ~0x10;  // enable current to motor always(set bit= 0)
  }
  
  // set all unused (even unbonded) ports to inputs with pullups
  DDRA = 0x00;
  DDRB &= 0x10;
  DDRE = 0x00;
  PUCR |= 0x13;    // enable pullups for ports E, B and A
  DDRP = 0x00;
  PERP = 0xFF;     // enable pullup resistance for port P 
  DDRJ &= 0x3F;
  PERJ |= 0xC0;    // enable pullup resistance for port J6,7
  DDRS &= 0xF3;
  PERS |= 0x0C;    // enable pullup resistance for port S2,3
  // set spare port on port E to output if it is to be used
  if(inpram.spr_port[1])
    DDRE |= 0x10;
  // set port E, bit 1 as output (for knock enable)
  DDRE |= 0x02;

  reinit_flag = 0;

  // set up CRG RTI Interrupt for .128 ms clock. CRG from 8MHz oscillator.
  mms = 0;        // .128 ms tics
  millisec = 0;   // 1.024 ms clock (8 tics) for adcs
  lmms = 0;
  ltch_lmms = 0;
  outpc.seconds = 0;   // (1.0035) secs
  burn_flag = 0;
  RTICTL = 0x10;   // load timeout register for .128 ms (smallest possible)
  CRGINT |= 0x80;  // enable interrupt
  CRGFLG = 0x80;   // clear interrupt flag (0 writes have no effect)

  // Set up SCI (rs232): SCI BR reg= BusFreq(=24MHz)/16/baudrate
  SCI0BDL = (unsigned char)(1500000/inpram.baud);
  ltmp = (150000000/inpram.baud) - ((long)SCI0BDL*100);
  if(ltmp > 50)SCI0BDL++;   // round up 
  SCI0CR1 = 0x00;
  SCI0CR2 = 0x24;   // TIE=0,RIE = 1; TE=0,RE =1
  txcnt = 0;
  rxcnt = 0;
  txmode = 0;
  txgoal = 0;
  rcv_timeout = 0xFFFFFFFF;
  tble_addr[0] = (unsigned int *)&cltfactor_table;
  no_tble_words[0] = sizeof(cltfactor_table) / 2;
  tble_addr[1] = (unsigned int *)&matfactor_table;
  no_tble_words[1] = sizeof(matfactor_table) / 2;
  tble_addr[2] = (unsigned int *)&egofactor_table;
  no_tble_words[2] = sizeof(egofactor_table) / 2;

  //  Initialize timers:
  //   Note: Inj OC and PWM are Nanded.  
  //    -when turn on inj1 i/o, also enable pwm1 @ 100 % duty & 
  //      set pwm1 timer = 0 (.1 ms units).
  //    -When pwm1 timer = InjPWMTim, set duty cycle to InjPWMDty.
  //    -when done injecting, turn off inj1 i/o and pwm1. 
  //   TC0: Input capture (tach) - no pullup (default) for MSII
  //   TC1: Output compare for injector output - bank 1
  //   TC2: PWM2 for injector bank 1
  //   TC3: Output compare for injector output - bank 2
  //   TC4: PWM4 for injector bank 2
  //   TC5: Ouput compare for ignition output
  SPK = 1 - inpram.spkout_hi_lo;  // 0=spk low, but inverted logic
  CHG = 1 - SPK;                  // after transistor
  TIOS |= 0x3E; // Timer ch 0 = IC, ch 1-5 = OC & PWM, 
                   // ch 6,7 = I/O output
  // Set prescaler to 16. This divides bus clk (= PLLCLK/2 = 24 MHz)
  //   by 16. This gives 1.5 MHz timer, 1 tic = 2/3 us.
  TSCR2 = 0x04;
  TCTL2 |= 0x88;   // bit OM1,3,5 = 1. OC output line high or 
	               // low iaw OL1,3,5 (not toggle or disable)
  TCTL1 |= 0x08;
  TSCR2 |= 0x80;   // enable timer overflow interrupt (TOI)
  
  // Set up injector PWMs - PWM2, 4
  MODRR = 0x14;     // Make Port T pins 2,4 be PWM
  PWME = 0;         // disable pwms initially
  PWMPOL = 0x14;    // polarity = 1, => go hi when start
  PWMCLK = 0x14;    // select scaled clocks SB, SA
  PWMPRCLK = 0x00;  // prescale A,B clocks = bus = 24 MHz
  PWMSCLA = 0x0C;   // pwm clk = SA clk/(2*SCLA) = 24MHz/24 = 1 us clk
  PWMSCLB = 0x0C;   // pwm clk = SB clk/(2*SCLB) = 24MHz/24 = 1 us clk
  PWMCAE = 0x00;    // standard left align pulse: -----
                    //                           |     |______
                    //                             duty
                    //                           <---period---> 
  PWMPER2 = inpram.InjPWMPd;   // set PWM period (us)
  PWMPER4 = inpram.InjPWMPd;   // set PWM period (us)
  
  ign_reset();
  first_edis = 1;
  
  // IACStart = enough steps to set IAC wide open.
  //  Set current IAC position to IACStart (all way closed), then move
  //  to the 0 position (wide open - fast idle) and zero out. After 
  //  this all subsequent IAC commands will gradually close air passage
  //  as clt temp rises.
  //  Note: Ignore all move commands until finished current move.
  IAC_moving = 0;
  motor_step = -1;
  if(inpram.IdleCtl == 4) IACmotor_reset = 1;
     else IACmotor_reset = 0;
  outpc.iacstep = inpram.IACStart * inpram.IACScale;  // set current motor step pos
                       //  to closed since don't have actual position.
  IACmotor_pos = 0;    // command motor step position to wide open
  move_IACmotor();
  last_iacclt = -3200;
  iacpwmctr = 0;

  // set up ADC processing
  // ATD0
  //    - AN0 is MAP
  //    - AN1 is MAT
  //    - AN2 is CLT
  //    - AN3 is TPS
  //    - AN4 is BAT
  //    - AN5 is EGO1 (NB or non-MS single chan WB)
  //    - AN6 is Spare or BARO (BaroOption =2) or EGO2 (EgoOption = 3)
  //    - AN7 is Spare or KNOCK
  // Set up ADCs so they continuously convert, then read result registers
  //  every millisecond
  first_adc = 2;		// use 2nd ADC reading for BaroOption=1
  ATD0CTL2 = 0x40;  // leave interrupt disabled, set fast flag clear
  ATD0CTL3 = 0x00;  // do 8 conversions/ sequence
  ATD0CTL4 = 0x67;  // 10-bit resoln, 16 tic cnvsn (max accuracy),
                    // prescaler divide by 16 => 1 us tic x 18 tics
  ATD0CTL5 = 0xB0;  // right justified,unsigned, continuous cnvsn,
                    // sample 8 channels starting with AN0
  ATD0CTL2 = 0x80;  // turn on ADC0

// Initialize variables
  flocker = 0;  
  Rpm_Coeff = 120000000 / inpram.no_cyl;
  pwcalc1 = 0;    // us
  outpc.pw1 = pwcalc1;
  pwcalc2 = 0;    // us
  outpc.pw2 = pwcalc2;
  outpc.adv_deg = 0;    // crank deg x 10
  ic_adv_deg = outpc.adv_deg - inpram.adv_offset;
  coil_dur = inpram.max_coil_dur;   // msx10
  coil_dur_set = coil_dur * 100;    // us
  outpc.coil_dur = (int)(coil_dur_set / 100);   // msx10
  last_tps = 100;        // % x 10
  last_map = 700;       // kPa x 10
  outpc.afrtgt1 = 147;  // afr x 10
  outpc.afrtgt2 = outpc.afrtgt1;
  outpc.map = 700;      // kPa x 10
  outpc.baro = 1010;    // kPA X 10
  if(inpram.Temp_Units == 0)  {
  	outpc.mat = 700;      // degF x 10
  	outpc.clt = 700;      // degF x 10
  }
  else  {
  	outpc.mat = 200;      // degC x 10
  	outpc.clt = 200;      // degC x 10
  }
  outpc.tps = 100;      // % x 10
  outpc.batt = 130;     // volts x 10
  outpc.aircor = 100;
  outpc.vecurr1 = 100;
  outpc.vecurr2 = outpc.vecurr1;
  outpc.barocor = 100;
  outpc.warmcor = 100;
  outpc.cold_adv_deg = 0;  // crank deg x 10
  outpc.wbo2_en1 = 1;
  outpc.wbo2_en2 = 1;
  outpc.ego1 = 147;        // afr x 10
  outpc.ego2 = 147;        // afr x 10
  outpc.egocor1 = 100;
  outpc.egocor2 = 100;
  outpc.tpsfuelcut = 100;
  outpc.gammae = 100;
  outpc.tpsaccel = 0;
  outpc.knock = 0;

  // make IC highest priority interrupt
  HPRIO = 0xEE;

  // enable global interrupts
  ENABLE_INTERRUPTS
  // Prime Pulse - shoot 1 prime pulse of length PrimeP ms x 10
  if(inpram.PrimeP)  {
  	outpc.pw1 = inpram.PrimeP * 100;       // us
  	outpc.pw2 = outpc.pw1;
  	// Turn on fuel pump
  	PTM |= 0x01;
  	// Turn On injectors & PWMs
  	TCTL2 |= 0x44;   // set outputs hi in OL1,3
  	CFORC |= 0x0A;   // force high
  	// set PWM duty (on time) to 100 %
  	PWMDTY2 = PWMPER2;
  	PWMDTY4 = PWMDTY2;
  	pwm1_on = 0;
  	pwm1_time = 0;
  	pwm2_on = 0;
  	pwm2_time = 0;
  	PWMCNT2 = 0x00;
  	PWMCNT4 = 0x00;
  	PWME |= 0x14;     // enable PWMs
  	// Set up to turn Off injectors when get to pw us
  	Fl1TimerComp = TCNT + ((3*(long)outpc.pw1)>>1);     // 2/3 us
  	TCTL2 &= ~0x44;   // set outputs lo in OL1,3
  	Fl1OCt_overflow = Fl1TimerComp >> 16; 
  	Fl1TimerComp &= 0xFFFF;
  	Fl2TimerComp = Fl1TimerComp;
  	Fl2OCt_overflow = Fl1OCt_overflow;
  	TC1 = (unsigned short)Fl1TimerComp;     // load OC compare register
  	TC3 = (unsigned short)Fl2TimerComp;     // load OC compare register
  	if(Fl1OCt_overflow <= 1)  {
  	  Fl1OCt_overflow = 0;
  	  Fl2OCt_overflow = 0;
		  // Enable OC interrupt
		  TIE |= 0x0A;
  	}
  	else  {  // Disable OC interrupt
  		TIE &= ~0x0A;
  		TFLG1 = 0x0A;   // clear OC interrupt flag
  	}
  	// turn on inj led
  	PTM |= 0x08;
  	outpc.squirt |= 0x03;   // both injectors squirting
  	outpc.engine |= 0x01;   // engine in ready to run status
  	// Note: prime pulse should be over (25 ms max) before cranking starts
  }

  //  main loop
  while(1)  {
  
// read all 10-bit ADC results & convert to engineering units
  adcval = (long)inpram.mat0 + 
    ((long)inpram.matmult * matfactor_table[ATD0DR1]) / 100; // deg F or C x 10
  outpc.mat = (short)adcval;
  adcval = (long)inpram.clt0 + 
    ((long)inpram.cltmult * cltfactor_table[ATD0DR2]) / 100; // deg F or C x 10
  outpc.clt = (short)adcval;
  adcval = (long)inpram.batt0 + 
    ((long)(inpram.battmax - inpram.batt0) * ATD0DR4) / 1023; // V x 10
  outpc.batt = (short)adcval;
  if(inpram.EgoOption >= 1)  {
  	adcval = (long)inpram.ego0 + 
  	  ((long)inpram.egomult * egofactor_table[ATD0DR5]) / 100; // afr x 10
  	outpc.ego1 = (short)adcval;
  }
  if(inpram.EgoOption == 3)  {
  	adcval = (long)inpram.ego0 + 
  	  ((long)inpram.egomult * egofactor_table[ATD0DR6]) / 100; // afr x 10
  	outpc.ego2 = (short)adcval;
  }
  else
  	outpc.ego2 = outpc.ego1; 
  if(inpram.BaroOption == 0)  {
  	outpc.barocor = 100;
  }
  if((inpram.BaroOption == 1) && (first_adc > 0))  {
  	adcval = (long)inpram.map0 + 
  	  ((long)(inpram.mapmax - inpram.map0) * ATD0DR0) / 1023; // kPa x 10
  	outpc.baro = (short)adcval;        // kPa x 10
  	first_adc--;
  };
  if(inpram.BaroOption == 2)  {
  	adcval = (long)inpram.baro0 + 
  	  ((long)(inpram.baromax - inpram.baro0) * ATD0DR6) / 1023; // kPa x 10
    outpc.baro = (short)adcval;
  }
  adcval = (long)inpram.knock0 + 
    ((long)(inpram.knockmax - inpram.knock0) * ATD0DR7) / 1023; // V x 10
  outpc.knock = (short)adcval;
  
  	if(inpram.BaroOption)                       // barometric correction (%)
  		outpc.barocor = barocor_table(outpc.baro);
  	outpc.aircor = aircor_table(outpc.mat);     // airdensity correction (%)

  /* Determine if in Speed-density or Alpha-N mode. If in Alpha-N mode,
    set the variable "kpa" = "tps". This will not break anything, since this 
    check is performed again when multiplying MAP against the enrichments, 
    and the SCI version of the variable is MAP, not kpa.  */
    if(!inpram.FuelAlpha)  {
    	kpa = outpc.map;                             // kpa x 10
    }
    else if(outpc.rpm < inpram.alpha_lorpm)  {
    	kpa = alphcalc_table(outpc.rpm,outpc.tps);   // kpa x 10
    }
    else if(outpc.rpm > inpram.alpha_hirpm)  {
    	kpa = outpc.map;                             // kpa x 10
    }
    else  {    
    	beta = ((long)100 * (outpc.rpm - inpram.alpha_lorpm)) / 
    		(inpram.alpha_hirpm - inpram.alpha_lorpm);
    	kpa = (short)((alphcalc_table(inpram.alpha_lorpm,outpc.tps) * 
    		(100 - beta)) / 100);
    	kpa =(short)(kpa + ((outpc.map * beta) / 100));
    }

  	// check for stall
  	if(((outpc.engine & 0x01) == 0) || (outpc.rpm == 0))goto BURN_FLASH;

  	// check idle control
  	if(inpram.IdleCtl == 1)  {
  		if(first_adc > 0)	 {
  			if(outpc.clt < inpram.FastIdle)
  			  PTM |= 0x02;              // turn on fast idle solenoid
  		}
  		if(outpc.clt < inpram.FastIdle - inpram.IdleHyst)
  			PTM |= 0x02;                // turn on fast idle solenoid
  		else if(outpc.clt > inpram.FastIdle)
  			PTM &= ~0x02;               // turn off fast idle solenoid
  	}
  	else if(IACmotor_reset && ((inpram.IdleCtl == 2) ||
  	                           (inpram.IdleCtl == 3) ||
  	                           (inpram.IdleCtl == 4)))  {
  		if((outpc.clt < last_iacclt - inpram.IdleHyst) ||
  		   (outpc.clt > last_iacclt))  {
  		  IACmotor_pos = iaccmd_table(outpc.clt);
  		  if(outpc.iacstep != IACmotor_pos)  {
  			  // move IAC motor to new step position
  			  if(move_IACmotor())
  			    last_iacclt = outpc.clt;
  		  }
  		}
   	}

/**************************************************************************
**
** Cranking Mode
**
** Pulsewidth is directly set by the coolant temperature to a value of
**  CWU (at temp_table[0]) and CWH (at temp_table[NO_TEMPS -1]).
**  The value is interpolated at clt.
**
**************************************************************************/
  	if(outpc.rpm < CRANK_RPM)  {
  		outpc.engine |= 0x02;    // set cranking bit
  		outpc.engine &= ~0x0C;   // clr starting warmup bit & warmup bit
  		if(outpc.tps > inpram.TPSWOT)  {
  			pwcalc1 = 300;    // usec  (.3 ms for Flood Clear)
  			pwcalc2 = pwcalc1;
  			goto DIST_ADV;
  		}
  		// KJW if outside the interpolation range, rail to CWU or CWH as 
  		// appropriate, else interpolate
  		if(outpc.clt >= inpram.temp_table[NO_TEMPS-1])
  		  pwcalc1 = (unsigned short)inpram.CWH * 100;
  		else if(outpc.clt <= inpram.temp_table[0])
  		  pwcalc1 = (unsigned short)inpram.CWU * 100;
  		else {
  		  ltmp = (outpc.clt - inpram.temp_table[0]) * (long)(inpram.CWH - 
  		    inpram.CWU) * 100;
  		  pwcalc1 = (unsigned short)(inpram.CWU * (long)100 + (ltmp / 
  		    (inpram.temp_table[NO_TEMPS-1] - inpram.temp_table[0]))); // usec
  		}
  		pwcalc2 = pwcalc1;
  		goto DIST_ADV;	
  	}
/**************************************************************************
**
** Warm-up and After-start Enrichment Section
**
** The Warm-up enrichment is a linear interpolated value for the current clt
** temperature from warmen_table[NO_TEMPS] vs temp_table[NO_TEMPS]. The 
** interpolation is done in subroutine warmcor_table.
**
** Also, the after-start enrichment value is calculated and applied here - it
**  is an added percent value on top of the warmup enrichment, and it is applied
**  for the number of ignition cycles specified in AWC. This enrichment starts
**  at a value of AWEV at first, then it linearly interpolates down to zero
**  after AWC cycles.
**
**  If (startw, engine is set) then:
**   compare if (AWC > 0) then:
**    interpolate for warmup enrichment
**   else clear startw bit in engine
**
**************************************************************************/
  	wrmtmp = outpc.warmcor; 
  	if(outpc.engine & 0x02)  {     // if engine cranking
  		outpc.engine &= ~0x02;       // clear crank bit
  		outpc.engine |= 0x0C;        // set starting warmup bit & warmup bit
  		asecount = 0;
  	}
  	wrmtmp = warmcor_table(outpc.clt);          // %
  	outpc.cold_adv_deg = cold_ign_table(outpc.clt);    // deg x 10 

  	if(wrmtmp == 100)  {     // done warmup
  		outpc.engine &= ~0x0C;        // clear start warmup bit & warmup bit  		
  		PTM &= ~0x20;                 // clear warmup led
  		goto END_WRM;
  	}
  	PTM |= 0x20;            	      // set warmup led
  	outpc.engine |= 0x08;           // set warmup bit
  	if(!(outpc.engine & 0x04))      // if starting warmup bit clear
  		goto END_WRM;
  	if(asecount > inpram.AWC)  {
  		outpc.engine &= ~0x04;        // clear start warmup bit
  		goto END_WRM;
  	}
  	if(inpram.AWC > 0)  {
  		utmp1 = inpram.AWEV * asecount;
  		wrmtmp += (inpram.AWEV - (utmp1 / inpram.AWC));
  	}
END_WRM:
    outpc.warmcor = wrmtmp;
/**************************************************************************
**
**  Throttle Position Acceleration Enrichment
**
**   Method is the following:
**
**
**   ACCELERATION ENRICHMENT:
**   If (tpsdot < 0) goto DEACCELERATION_ENRICHMENT
**   If tpsdot > tpsthresh and TPSAEN bit = 0 then (acceleration enrichment):
**   {
**    1) Set acceleration mode
**    2) Continuously determine rate-of-change of throttle, and perform
**        interpolation of table values to determine acceleration
**        enrichment amount to apply.
**   }
**   If (TPSACLK > TpsAsync) and TPSAEN is set then:
**   {
**    1) Clear TPSAEN bit in engine
**    2) Set TPSACCEL to 0
**    3) Go to EGO Delta Step Check Section
**   }
**   Enrichment tail-off pulsewidth:
**   
**   ------------------ tpsaccel
**   |            |\
**   |            |   \
**   |            |      \
**   |            |         \		____ TpsAccel2
**   |            |            | 
**   |            |            |
**   ---------------------------
**   <--TpsAsync--><-TpsAsync2->
**
**
**   DEACCELERATION ENRICHMENT:
**   If (-tpsdot) > tpsthresh then (deceleration fuel cut)
**   {
**    If (TPSAEN = 1) then:
**    {
**      1) TPSACCEL = 0 (no acceleration)
**      2) Clear TPSAEN bit in ENGINE
**      3) Go to EGO Delta Step
**    }
**    If (RPM > 1500 then (fuel cut mode):
**    {
**      1) Set TPSACCEL value to TPSDQ
**      2) Set TPSDEN bit in ENGINE
**      3) Go to EGO Delta Step Check Section
**    }
**   }
**   else
**   {
**    If (TPSDEN = 1) then
**    {
**     1) Clear TPSDEN bit in ENGINE
**     2) TPSACCEL = 0
**     3) Go to EGO Delta Step Check Section
**    }
**   }
**
**************************************************************************/
  tpsatmp = outpc.tpsaccel;
  DISABLE_INTERRUPTS
  tpsdot_ltch = outpc.tpsdot;
  mapdot_ltch = outpc.mapdot;
  ENABLE_INTERRUPTS
  if(tpsdot_ltch < 0)goto TDE;     // we are decelerating
  if(((inpram.Tps_acc_wght == 0) || (tpsdot_ltch < inpram.TpsThresh)) &&
     ((inpram.Tps_acc_wght == 100) || (mapdot_ltch < inpram.MapThresh)))
  		goto TAE_CHK_TIME;
  if(outpc.engine & 0x10)goto AE_COMP_SHOOT_AMT;   // if accel enrich bit set
  // start out using first element - determine actual next time around
  tpsatmp = (((short)inpram.tpsen_table[0] * inpram.Tps_acc_wght) + 
    ((short)inpram.mapen_table[0] * (100 - inpram.Tps_acc_wght))) / 100;
  tpsaclk = 0;          	    // incremented in .1 sec timer
  outpc.engine |= 0x10;       // set tpsaen bit
  outpc.engine &= ~0x20;      // clear tpsden bit
  PTM |= 0x10;        	      // set accel led
  goto END_TPS;
/* First, calculate Cold temperature add-on enrichment value from coolant value 
  TPSACOLD at min temp & 0 at high temp.

 Then determine cold temperature multiplier value ACCELMULT (in percent).

 Next, Calculate Shoot amount (quantity) for acceleration enrichment from table.
 Find bins (between) for corresponding TPSDOT and MAPDOT, and linear interpolate
 to find enrichment amount from table. This is continuously
 checked every time thru main loop while in acceleration mode,
 and the highest value is latched and used.

 The final acceleration enrichment (in .1 ms units) applied is AE = 
 (((Alookup(TPSDOT)*Tps_acc_wght + Alookup(MAPDOT)*(100 - Tps_acc_wght))
    /100) * ACCELMULT/100) + TPSACOLD.  
*/

AE_COMP_SHOOT_AMT:
  
 if(tpsaclk <= inpram.TpsAsync)  {
  tmp1 = (short)inpram.Tpsacold  - (short)((inpram.Tpsacold * 
      (long)(outpc.clt - inpram.temp_table[0]) / (inpram.temp_table[NO_TEMPS-1] 
      - inpram.temp_table[0])));	    // in .1 ms
  tmp2 = (short)inpram.AccMult + (short)(((100 - inpram.AccMult) * 
      (long)(outpc.clt - inpram.temp_table[0]) / (inpram.temp_table[NO_TEMPS-1] 
      - inpram.temp_table[0])));	    // in %
  if(inpram.Tps_acc_wght > 0)  {
  	tmp3 = tpscor_table(tpsdot_ltch);   // .1 ms units  
  }
  else
  	tmp3 = 0;
  if(inpram.Tps_acc_wght < 100)  {
  	tmp4 = mapcor_table(mapdot_ltch);   // .1 ms units  
  }
  else
  	tmp4 = 0;
  tmp3 = ((tmp3 * inpram.Tps_acc_wght) + (tmp4 * (100 - inpram.Tps_acc_wght))) / 100;
  tmp3 = (tmp3 * tmp2) / 100;
  if(tmp3 > 200)
            tmp3 = 200;  // rail at 20 ms
  tmp3 += tmp1;
  if(tmp3 > tpsatmp)
       tpsatmp = tmp3; // make > tps/mapen_table entry for lowest tps/mapdot
                     // plus latch and hold largest pw
  tpsaccel_end = tpsatmp;		// latch last tpsaccel before tailoff
 }
 else  {      // tailoff enrichment pulsewidth
  if(inpram.TpsAsync2 > 0)  {
    tpsatmp = tpsaccel_end + (short)(((inpram.TpsAccel2 - tpsaccel_end) *
            (long)(tpsaclk - inpram.TpsAsync)) / inpram.TpsAsync2);
  }
  else
    tpsatmp = 0;
 }

TAE_CHK_TIME:
  // check if accel is done
  if(!(outpc.engine & 0x20))   {   // if tps decel bit not set
  	if(tpsaclk < (inpram.TpsAsync + inpram.TpsAsync2))goto END_TPS;
  }
  outpc.engine &= ~0x10;     // clear tps accel bit
  outpc.tpsfuelcut = 100;
  tpsatmp = 0;
  PTM &= ~0x10;              // clear accel led
  outpc.engine &= ~0x20;     // clear tps decel bit
  goto END_TPS;

TDE:
  // decel
  if((-tpsdot_ltch) < inpram.TpsThresh)goto TDE_CHK_DONE;
  if(outpc.engine & 0x10)  {   // if tps accel bit set
  	outpc.tpsfuelcut = 100;
  	tpsatmp = 0;
  	outpc.engine &= ~0x10;     // clear tps accel bit
  	PTM &= ~0x10;              // clear accel led
  	outpc.engine &= ~0x20;     // clear tps decel bit
  	goto END_TPS;
  }
  if(outpc.rpm < 1500)goto END_TPS;
  outpc.tpsfuelcut = inpram.TPSDQ;    // in %
  outpc.engine |= 0x20;		  // set tps decel bit
  outpc.engine &= ~0x10;    // clear tps accel bit
  PTM &= ~0x10;		          // clear accel led
  goto END_TPS;

TDE_CHK_DONE:
  if(outpc.engine & 0x20)  {     // if decelerating
  	outpc.engine &= ~0x20;       // clear tps decel bit
  	outpc.tpsfuelcut = 100;
  	tpsatmp = 0;
  }
END_TPS:
  outpc.tpsaccel = tpsatmp; 	  

/**************************************************************************
**
**  Exhaust Gas Oxygen Sensor Measurement Section
**
**************************************************************************/
  	if( (inpram.EgoOption == 0) ||
      	    (outpc.rpm < inpram.RPMOXLimit) ||
      	    (outpc.engine & 0x30) ||                // engine accel/ decel
      	    (outpc.clt < inpram.EgoTemp) ||
      	    (outpc.tps > inpram.TPSOXLimit) ||
        	    (outpc.map > inpram.MAPOXLimit) ||
        	    (outpc.seconds < 30))  {
		  outpc.egocor1 = 100;
		  outpc.egocor2 = outpc.egocor1;
		  goto VETABLELOOKUP;
  	}
  	if(egocount < inpram.EgoCountCmp)goto VETABLELOOKUP;
  	// check if rich/ lean after every EgoCountCmp ignition pulses
  	egocount = 0;
  	if(inpram.EgoOption  == 1)  {
  		// NBO2
  		if(outpc.ego1 > inpram.AFRTarget)  {
  			// O2_IS_LEAN
  			if((outpc.egocor1 + inpram.EgoStep) <= (100 + inpram.EgoLimit))
  				outpc.egocor1 += inpram.EgoStep;      // egocor not railed
  		}
  		else  {				
  			// O2_IS_RICH
  			if((outpc.egocor1 - inpram.EgoStep) >= (100 - inpram.EgoLimit))
  				outpc.egocor1 -= inpram.EgoStep;      // egocor not railed
  		}
  		outpc.egocor2 = outpc.egocor1;
  	}
  	else if(inpram.EgoOption  >= 2)  {
  		// WBO2
		  outpc.afrtgt1 = (unsigned char)afrcalc_table(outpc.rpm, kpa, 0); 
			                                                       // afr x 10
		  if(inpram.dual_tble_optn)
				outpc.afrtgt2 = (unsigned char)afrcalc_table(outpc.rpm, kpa, 1);
			                                                       // afr x 10
		  else
			  outpc.afrtgt2 = outpc.afrtgt1;
  		// first channel
  		if(!outpc.wbo2_en1)  {
  			outpc.egocor1 = 100;
  			goto SCNDEGO;
  		}
  		egostep = ((outpc.ego1 - outpc.afrtgt1) * 100)/outpc.afrtgt1;
  		if(egostep > 0)  {
  			// O2_IS_LEAN
  			if((outpc.egocor1 + egostep) <= (100 + inpram.EgoLimit))
  				outpc.egocor1 += egostep;      // egocor not railed
  			else
  			  outpc.egocor1 = 100 + inpram.EgoLimit;    // railed
  		}
  		else  {				
  			egostep = -egostep;
  			// O2_IS_RICH
  			if((outpc.egocor1 - egostep) >= (100 - inpram.EgoLimit))
  				outpc.egocor1 -= egostep;      // egocor not railed
  			else
  			  outpc.egocor1 = 100 - inpram.EgoLimit;    // railed
  		}
SCNDEGO:
  		if(!outpc.wbo2_en2)  {
  			outpc.egocor2 = 100;
  			goto VETABLELOOKUP;
  		}
  		egostep = ((outpc.ego2 - outpc.afrtgt2) * 100)/outpc.afrtgt2;
  		if(egostep > 0)  {
  			// O2_IS_LEAN
  			if((outpc.egocor2 + egostep) <= (100 + inpram.EgoLimit))
  				outpc.egocor2 += egostep;      // egocor not railed
 			else
  			  outpc.egocor2 = 100 + inpram.EgoLimit;    // railed
  		}
  		else  {				
  			egostep = -egostep;
  			// O2_IS_RICH
  			if((outpc.egocor2 -egostep) >= (100 - inpram.EgoLimit))
  				outpc.egocor2 -= egostep;      // egocor not railed
  			else
  			  outpc.egocor2 = 100 - inpram.EgoLimit;    // railed
  		}
  	}

VETABLELOOKUP:
  	// Look up volumetric efficiency as function of rpm and map (tps
  	//  in alpha N mode)
  	outpc.vecurr1 = vecalc_table(outpc.rpm, kpa, 0);   // in %
  	if(inpram.dual_tble_optn)
  		outpc.vecurr2 = vecalc_table(outpc.rpm, kpa, 1);   // in %
  	else  {
  		outpc.vecurr2 = outpc.vecurr1;
  	}

/**************************************************************************
**
** Computation of Fuel Parameters
** Note that GAMMAE only includes Warm, Tpsfuelcut, Barocor, and Aircor 
** (EGO no longer included)
**
**************************************************************************/
  	lsum = ((outpc.warmcor * outpc.tpsfuelcut) / 100);
  	lsum = (lsum * ((outpc.barocor * outpc.aircor) / 100)/100);
  	outpc.gammae = (int)lsum;
  	if(inpram.FuelAlpha != 2)
  	  lsum2 = (long)kpa;
  	else
  	  lsum2 = 1000;	         // normalizes to ~1 when divide by baro 
  	lsum1 = (lsum * ((outpc.egocor1 * lsum2) / outpc.baro)/100);
  	lsum1 = (lsum1 * ((outpc.vecurr1 * (long)inpram.ReqFuel)/ 100)/ 100); // usec
  	lsum2 = (lsum * ((outpc.egocor2 * lsum2) / outpc.baro)/100);
  	lsum2 = (lsum2 * ((outpc.vecurr2 * (long)inpram.ReqFuel)/ 100)/ 100); // usec

/**************************************************************************
**
** Calculation of Battery Voltage Correction for Injector Opening Time
**
** Injector open time is implemented as a linear function of
**  battery voltage, from 7.2 volts to 19.2 volts,
**  with 13.2 volts being the nominal operating voltage
**
** INJOPEN = injector open time at 13.2 volts in ms x 10
** BATTFAC = injector open adjustment factor 6 volts from 13.2V in ms x 10
**
**
** + (INJOPEN + BATTFAC)
** +   *
** +                     (INJOPEN)
** +                         *
** +                                       (INJOPEN - BATTFAC)
** +                                               *
** +
** ++++++++++++++++++++++++++++++++++++++++++++++++++++++
**    7.2V                 13.2V                19.2
**
**************************************************************************/
  	pw_open = (inpram.InjOpen + inpram.BatFac) - (inpram.BatFac * 
  				(outpc.batt - 72) / 60);         // ms x 10
/**************************************************************************
**
** Calculation of Final Pulse Width
**
**************************************************************************/
  	lsum = (pw_open + outpc.tpsaccel) * 100;     // usec
  	lsum1 += lsum;      // usec
  	if(lsum1 > 32000)lsum1 = 32000; // rail at 32 ms
  	pwcalc1 = (unsigned int)lsum1;           
  	lsum2 += lsum;      // usec
  	if(lsum2 > 32000)lsum2 = 32000;
  	pwcalc2 = (unsigned int)lsum2;

/**************************************************************************
**
** Calculation of Distributor Advance & coil charge time correction
**
**************************************************************************/
DIST_ADV:
  	lsum = ign_table(outpc.rpm, kpa) + outpc.cold_adv_deg;   // deg x 10
  	if(inpram.RevLimOption == 1)  {
  	  if(outpc.rpm > inpram.RevLimRpm2)
  	    lsum -= inpram.RevLimMaxRtd;                  // deg x 10
  	  else if(outpc.rpm > inpram.RevLimRpm1)  {
  	    lsum -= (((long)inpram.RevLimMaxRtd*(outpc.rpm- inpram.RevLimRpm1))/
  	         (inpram.RevLimRpm2 - inpram.RevLimRpm1));		// deg x 10
  	  }
  	}
  	ic_adv_deg = (int)(lsum - inpram.adv_offset);   // relative to IC
  	outpc.adv_deg = (int)lsum;			// relative to true TDC
  	// Base dwell
  	tmp1 = inpram.max_coil_dur;
  	// Add correction if accelerating
  	if(outpc.tpsaccel > 0)
  	  tmp1 += inpram.DurAcc;
  	// Correct for battery voltage
  	tmp1 += coil_dur_table(outpc.batt - 120);
  	coil_dur = tmp1;			// msx10, will be used in IC Isr.
  	// following belongs in Input Capture ISR, but this saves long div in ISR
  	outpc.coil_dur = (int)(coil_dur_set / 100);   // msx10
/***************************************************************************
**
** Check whether to burn flash
**
**************************************************************************/
BURN_FLASH:
    if(burn_flag > 0)  {
      // inpram,flash comprised of three 512 byte(256 word) sectors
      fburner((unsigned int *)&inpflash,(unsigned int *)&inpram,
               sizeof(inpflash)/2);
      if(burn_flag >= sizeof(inpflash)/2)  {
        burn_flag = 0;
        flocker = 0;
      }
      else
        burn_flag++;
    }
/***************************************************************************
**
** Check for reinit command
**
**************************************************************************/
    if(reinit_flag)  {
      reinit_flag = 0;
      // update initialization of variables/ registers dependent on
      // user config inputs
      Rpm_Coeff = 120000000 / inpram.no_cyl;
      SPK = 1 - inpram.spkout_hi_lo;  // 0=spk low, but inverted logic
      CHG = 1 - SPK;                  // after transistor
      if(!(inpram.ICIgnOption & 0x02))
        trig_ret_mode = 0;
      else if(outpc.rpm < CRANK_RPM)
        trig_ret_mode = 2;
      TCTL4 &= ~0x03;  // clear bits 0,1
      if(inpram.ICIgnOption & 0x01)  {
        // normal rising edge input  capture
        if(!trig_ret_mode)
          // not trig ret mode
          TCTL4 |= 0x01; // rising edge input capture
        else
          // reverse in trigger return mode
          TCTL4 |= 0x02; // falling edge input capture
      }
      else  {
        // normal falling edge input capture
        if(!trig_ret_mode)
          // not trig ret mode
          TCTL4 |= 0x02; // falling edge input capture
        else
          // reverse in trigger return mode
          TCTL4 |= 0x01; // rising edge input capture
      }
      // Set up SCI (rs232): SCI BR reg= BusFreq(=24MHz)/16/baudrate
      SCI0BDL = (unsigned char)(1500000/inpram.baud);
      ltmp = (150000000/inpram.baud) - ((long)SCI0BDL*100);
      if(ltmp > 50)SCI0BDL++;   // round up 
    }
/***************************************************************************
**
** Determine spare port settings
**
**************************************************************************/
  for(ix = 0;ix < NPORT; ix++)  {
    if(inpram.spr_port[ix])  {
      switch(inpram.condition[ix])  {
        case '<':
          if(*((int*)(&outpc) + inpram.out_offset[ix]) < inpram.thresh[ix])  {
            set_spr_port((char)ix, (inpram.port_val[ix] & 0x01));
          }
          else if(*((int*)(&outpc) + inpram.out_offset[ix]) > 
                         (inpram.thresh[ix] + inpram.hyst[ix]))  {
            set_spr_port((char)ix, 1 - (inpram.port_val[ix] & 0x01));
          }
          break;       
        case '>':
          if(*((int*)(&outpc) + inpram.out_offset[ix]) > inpram.thresh[ix])  {
            set_spr_port((char)ix, (inpram.port_val[ix] & 0x01));
          }
          else if(*((int*)(&outpc) + inpram.out_offset[ix]) < 
                         (inpram.thresh[ix] - inpram.hyst[ix]))  {
            set_spr_port((char)ix, 1 - (inpram.port_val[ix] & 0x01));
          }
          break;       
        case '=':
          if((*((int*)(&outpc) + inpram.out_offset[ix]) <= (inpram.thresh[ix]
             + inpram.hyst[ix])) &&  (*((int*)(&outpc) + inpram.out_offset[ix])
              >= (inpram.thresh[ix] - inpram.hyst[ix])))  {
            set_spr_port((char)ix, (inpram.port_val[ix] & 0x01));
          }
          else  {
            set_spr_port((char)ix, 1 - (inpram.port_val[ix] & 0x01));
          }
          break;       
        default:
          break;       
      }		// end switch
    }		 // end if spr_port
  }			 // end for ix loop
/***************************************************************************
**
**  Check for serial receiver timeout
**
**************************************************************************/
    if(lmms > rcv_timeout)  {
      txmode = 0;    // break out of current receive sequence
      rcv_timeout = 0xFFFFFFFF;
    }
    
  }     //  END Main while(1) Loop
}

void ign_reset(void)  {
  // enter here at init and when in stall condition
  // Disable IC, Ign,Inj OC interrupts
  TIE &= 0xD4;  
  TSCR1 &= 0x7F;      // disable all timers
  // reinitialize
  synch = 0;
  t_chgoff = 0xFFFFFFFF;
  outpc.engine = 0;
  outpc.rpm = 0;
  ICt_overflow = 0;
  IgnOCt_overflow = 0;
  Fl1OCt_overflow = 0;
  Fl2OCt_overflow = 0;
  TC_ovflow = 0;
  TC_ov_ix = 0;
  pulse_no = 0;  // This will inhibit overflow counters
  t_enable_IC = 0xFFFFFFFF;
  igncount = 0;
  altcount = 0;
  egocount = 0;
  tpsaclk = 0;
  asecount = 0;
  if(!(inpram.ICIgnOption & 0x02))
    trig_ret_mode = 0;
  else
    trig_ret_mode = 2;
  TCTL4 &= ~0x03;  // clear bits 0,1
  if(inpram.ICIgnOption & 0x01)  {
    // normal rising edge input  capture
    if(!trig_ret_mode)
      // not trig ret mode
	    TCTL4 |= 0x01; // rising edge input capture
    else
      // reverse in trigger return mode
	    TCTL4 |= 0x02; // falling edge input capture
  }
  else  {
    // normal falling edge input capture
    if(!trig_ret_mode)
      // not trig ret mode
	    TCTL4 |= 0x02; // falling edge input capture
    else
      // reverse in trigger return mode
	    TCTL4 |= 0x01; // rising edge input capture
  }
  // if coil charging, set flg to cease aft 1 s if no resynch
  t_chgoff = lmms + 7812;
  // Turn Off injectors
  TCTL2 &= ~0x44;   // set outputs lo in OL1,3
  CFORC |= 0x0A;  	
  PWME &= ~0x14;	  // turn off pwms
  // clear OC interrupt flags
  TFLG1 = (0x02 | 0x08 | 0x20);
  PTM &= ~0x08;           // turn off inj led
  outpc.squirt = 0;       // injectors off
  IgnOCpinstate = SPK;
  // enable timer & IC interrupt
  TSCR1 |= 0x80;
  TFLG1 = 0x01;
  TIE |= 0x01;
  return;
}

#ifndef GCC_BUILD
#pragma CODE_SEG  NON_BANKED
#endif
INTERRUPT void ISR_Timer_Clock(void)  {
unsigned char coils;
long adcval;
int map;
  // .128 ms clock interrupt - clear flag immediately to resume count
  CRGFLG = 0x80;  // clear RTI interrupt flag
  // also generate 1.024 ms, .10035 sec and 1.0035 sec clocks
  lmms++;   // free running clock(.128 ms tics) good for ~ 110 hrs
  mms++;    // in .128 ms tics - reset every 8 tics = 1.0024 ms
  
  // check for re-enabling IC interrupt
  if(lmms > t_enable_IC)  {
	  TFLG1 = 0x01;				// clear IC interrupt flag
    TIE |= 0x01;				// enable Ignition IC interrupt
    t_enable_IC = 0xFFFFFFFF;
  }
  if(lmms > t_chgoff)  {
	  // cease charging ignition coil after fuel has dispersed
	  TCTL1 = (TCTL1 & 0xFB) | (SPK << 2);  // set OC o/p in OL5
	  CFORC |= 0x20;           // force output in OL5 onto OC pin
	  IgnOCpinstate = SPK;
	  t_chgoff = 0xFFFFFFFF;
  }
  // check for turning on pwm duty cycle
  if(!(outpc.engine & 0x02))  {   // if engine not cranking
  	if(!pwm1_on)  {               // and haven't started pwm  
  		if(lmms > pwm1_time)  {
  			pwm1_on = 1;
  			PWMPER2 = inpram.InjPWMPd;   // set PWM period (us)
  			// set PWM duty (on time)
  			PWMDTY2 = (unsigned char)((inpram.InjPWMDty * PWMPER2) / 100);
  		}
  	}
  	if(!pwm2_on)  {               // and haven't started pwm  
  		if(lmms > pwm2_time)  {
  			pwm2_on = 1;
  			PWMPER4 = inpram.InjPWMPd;   // set PWM period (us)
  			// set PWM duty (on time)
  			PWMDTY4 = (unsigned char)((inpram.InjPWMDty * PWMPER4) / 100);
  		}
  	}
  }
  
  // check for IAC step pulses
  if(inpram.IdleCtl == 4) {
	iacpwmctr++;
	if (iacpwmctr >= 100) iacpwmctr=0;
	if (IACmotor_pos > iacpwmctr) PTM &= ~0x02;      // turn off fast idle solenoid
	else PTM |= 0x02;                         // turn on fast idle solenoid
  }
  else {
  	if(IAC_moving > 1)  {
  		if(lmms > motor_time)  {
  			motor_time = lmms + ((inpram.IACtstep*8)/10);  // .128 ms
  			motor_step++;
  			if(motor_step > 7)motor_step = 0;
  			if(IACmotor_pos > outpc.iacstep)  {   // cw
  				outpc.iacstep++;
  				coils = (IACCoilA[7-motor_step] << 6) | 
  					(IACCoilB[7-motor_step] << 7);
  			}
  			else  {                               //ccw
  				outpc.iacstep--;
  				coils = (IACCoilA[motor_step] << 6) | 
  					(IACCoilB[motor_step] << 7);
  			}
  			PTT = (PTT & 0x3F) | coils;
  			if(outpc.iacstep == IACmotor_pos)	 {
  				if(!IACmotor_reset)	 {
  				  IACmotor_reset = 1;
  				  last_iacclt = -3200;	 // this will cause to go to 
  				        // temperature based position after initial reset
  				  IAC_moving = 0;
  				}
  				else  {
  				  motor_time += 32000; // delay next move for ~4 secs
  				  IAC_moving = 1;
  				}
  				if(inpram.IdleCtl == 2)
  				  PORTB |= 0x10;  // disable current to stepper motor(bit=1)
  			}
  		}
  	} 
	  else if(IAC_moving)  {
  		if(lmms > motor_time)  {
  			IAC_moving = 0;
  		}
  	}
  }

  // check rpm for stall condition (< 50 rpm) after engine synch
  if(synch)  {
    if((lmms - ltch_lmms) > (18750/inpram.no_cyl))  {
  	  // Engine is stalled, clear all in engine
  	  PTM &= ~0x01;    	// Turn off fuel Pump
  	  PTM &= ~0x02;    	// Turn off fast idle ** Bug Fix By Guy Hill **
  	  ign_reset();
    }
  }
  // if no tach pulse within 2 sec
  else if((lmms - ltch_lmms) > 15625)  {
  	  PTM &= ~0x01;    	// Turn off fuel Pump
  	  PTM &= ~0x02;    	// Turn off fast idle ** Bug Fix By Guy Hill **
  }
  // check mms to generate other clocks
  if(mms < 8)goto CLK_DONE;
  mms = 0;
  
  millisec++;     // actually 1.024 ms
  
 if((millisec == 49) || (millisec >= 98))  {		// 50 ms calculations  
 // get map, tps
 // map rolling average
  adcval = (long)inpram.map0 + 
    ((long)(inpram.mapmax - inpram.map0) * ATD0DR0) / 1023; // kPa x 10
  map = (short)adcval;
  if(inpram.MapAveno <= 1)  {   // no averaging
    outpc.map = map;
  } 
  else if(map_avestate == 0)  {  // buffer not filled yet
    map_bufsum += map;
    map_buf[map_aveix] = map;
    map_aveix++;
    outpc.map = (int)(map_bufsum / map_aveix);
    if(map_aveix >= inpram.MapAveno)  {
      map_avestate = 1;
      map_aveix = 0;
    }
  }
  else if(map_avestate == 1)  {	 
    // buffer filled - add new value remove old
    map_bufsum += (map - map_buf[map_aveix]);
    map_buf[map_aveix] = map;
    map_aveix++;
    outpc.map = (int)(map_bufsum / inpram.MapAveno);
    if(map_aveix >= inpram.MapAveno)  {
      map_aveix = 0;
    }
  }
  adcval = (ATD0DR3 - (long)inpram.tps0) * 1000 / 
    (long)(inpram.tpsmax - inpram.tps0);           // % x 10            
  outpc.tps = (short)adcval;
  // save last tps, map for calculation of tpsdot, mapdot
  outpc.tpsdot = 2 * (outpc.tps - last_tps); // % x 10 change per .1 sec
  last_tps = outpc.tps;
  outpc.mapdot = 2 * (map - last_map);    // kPa x 10 change per .1 sec
  last_map = map;
 }                                        // end 50 ms calculations
 
  if(millisec < 98)goto CLK_DONE;
  millisec = 0;
  tpsaclk++;                      // .10035 sec clock
  
CLK_DONE:
  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT

#pragma CODE_SEG NON_BANKED                
#endif
INTERRUPT void ISR_Ign_TimerIn(void)  {

static unsigned long t_in2,t_in3,dt1,dt2,dt3;
long ddt2,ddt3,dddt3,tcnt;
static unsigned long dtpred_old;
static char missed_pulse, cut_fuel;
unsigned int sched_both,rpm_pred=0,rpm=0;
unsigned long ltmp1;
long coil_dur_calc,old_charge;

  // Just received new input pulse - 1/ cyl / 2 crank revs (1 for 2 stroke)
  if(pulse_no == 0)  {  // 1st input pulse
	  pulse_no++;
	  ICt_overflow = 0;
	  t_in3 = (((long)TC0<<1)/3);
	  dt2 = 0;
	  dt3 = 0;       // time difference between pulses, us
	  outpc.spare2 = (int)dt3;				// for HP calculations
	  ddt2 = 0;      // derivative of time difference
	  ddt3 = 0;      // derivative of time difference
	  dddt3 = 0;     // 2nd derivative of time difference
	  dtpred = dt3;  // predicted time difference for next pulse
	  ign_state = 0;
	  missed_pulse = 0;
	  cut_fuel = 0;
	  // clear IC interrupt flag
	  TFLG1 = 0x01;
	  return;
  }
  t_chgoff = 0xFFFFFFFF;
  ltch_lmms = lmms;     // latch RTI .128 ms clk
  // calculate new dt. All times in us.
  t_in2 = t_in3;
  dt1 = dt2;
  dt2 = dt3;
  t_in3 = (((long)TC0<<1)/3);
  if(trig_ret_mode == 1)  {
    // transitioning out of trigger return mode
    if(!(TFLG2 & 0x80) || (TCNT < TC0))  // check for t overflow isr pending
      ICt_overflow = 0;
    else
      ICt_overflow = -1;  // pending, so -1 will cause to clear in isr.
    // use last dt3 as prediction for next IC for this one time transition;
    //  also leave rpm as is.
    dtpred_old = dtpred;
    dtpred = dt3;
    trig_ret_mode = 0;	 // done with trig return til ign_reset
    goto AVE;
  }
  // Need to test for overflow because overflow ISR < priority than
  //  IC ISR and might be pending, so ICt_overflow not up to date.
  if(!(TFLG2 & 0x80) || (TCNT < TC0))  {  // check for t overflow isr pending
    // no pending overflow isr or IC occurred < overflow
    if(!ICt_overflow)
      dt3 = t_in3 - t_in2;
    else  {
      dt3 = t_in3 + ((long)ICt_overflow * 43690) - t_in2;
      ICt_overflow = 0;
    }
  }
  else  {
		// pending overflow isr and IC after overflow
    dt3 = t_in3 + ((long)(ICt_overflow + 1) * 43690) - t_in2;
    ICt_overflow = -1;      // pending, so -1 will cause to clear in isr
  }
  if(pulse_no >= inpram.no_skip_pulses)  {
    // after 1st few pulses, start checking for missed/ extra pulses
    if(dt3 < ((100 - inpram.PulseTol) * dt2) / 100)  {
	    // reject false trigger
	    t_in3 = t_in2;
	    dt3 = dt2;
	    outpc.spare2 = (int)dt3;
	    // clear IC interrupt flag
	    TFLG1 = 0x01;
	    return;                     // wait for next (true) pulse
    }
    ltmp1 = ((100 + inpram.PulseTol) * dt2) / 100;
    if(dt3 > ltmp1)  {
      missed_pulse++;
      if(missed_pulse > 2)  {
  	    PTM &= ~0x01;    	// Turn off fuel Pump
  	    PTM &= ~0x02;    	// Turn off fast idle ** Bug Fix By Guy Hill **
  	    ign_reset();
  	    return;
      }
  	  // make up for missing pulse - set dt3 to last dt2
  	  dt3 = dt2;
    }
    else  {
      missed_pulse = 0;
    }
  }
  outpc.spare2 = (int)dt3;
  
  // Predict next dt - account for accel + Kalman filter for random errors
  if(pulse_no >= 3)  {  //have enough pulses to calc all derivatives
    synch = 1;  
    if(!inpram.PredOpt)  {
      dtpred_old = dtpred;
      outpc.spare1 = (int)(dt3 - dtpred_old);
      dtpred = dt3;
    }
    else if((inpram.PredOpt == 3)|| 
      ((inpram.PredOpt == 2 ) && (dtpred_old > 2000)))  {  // < 7500 rpm with 8 cyl
      /*
       0th pulse --
                   |-  dt1 --
       1st pulse --          |- ddt2 --
                   |-  dt2 --          |- dddt3
       2nd pulse --          |_ ddt3 --
                   |         |
                   |-  dt3 --
                   |         |_ ddtpred
       3rd pulse --          |
                   |_ dtpred-
                   |
       4th pulse --

       dtn = tn - tn-1
       ddtn = (dtn - dtn-1) / ((dtn + dtn-1)/2)
       ddtn-1 = (dtn-1 - dtn-2) / ((dtn-1 + dtn-2)/2)
       dddtn = (ddtn - ddtn-1)/((((dtn + dtn-1)/2) + ((dtn-1 + dtn-2)/2))/2)
       ddtpred = ddtn + dddtn * dtn
       dtpred = dtn + ddtpred * dtn = dtn + (ddtn * dtn) + (dddtn * dtn * dtn)
      */
      ltmp1 = dt3 + dt2;
      if(ltmp1 > 0)
        ddt3 = (long)(2*(dt3 - dt2)*dt3) / (long)ltmp1; // calculate new derivative x dt3
      else
        ddt3 = 0;
      ltmp1 = dt2 + dt1;
      if(ltmp1 > 0)
        ddt2 = (long)(2*(dt2 - dt1)*dt3) / (long)ltmp1; // calculate prev derivative x dt3
      else
        ddt2 = 0;
      ltmp1 += dt3 + dt2;
      if(ltmp1 > 0)
        dddt3 = (long)(4*(ddt3 - ddt2)*dt3) / (long)ltmp1; // calc 2nd derivative x dt3
      else
        dddt3 = 0;
      dtpred_old = dtpred;
      dtpred = dt3 + ddt3 + dddt3;  // pred new time interval - time bet now and next plse                                                                                                                                                                                                                                    // predict next time interval
      // Kalman filter random error correction (proportional to difference
      //   (between current dt3 and last predicted dt3).
      outpc.spare1 = (int)(dt3 - dtpred_old);
      dtpred += (inpram.Dtpred_Gain * (long)(dt3 - dtpred_old) / 100);
    }  
    else  {  // > 7500 rpm with 8 cyl
      /*
       1st pulse --
                   |-  dt2 --
       2nd pulse --          |- ddt3
                   |-  dt3 -- 
       3rd pulse --          |
                   |- dtpred-
       4th pulse -- 

       dtn = tn - tn-1
       ddtn = (dtn - dtn-1) / dtn
       dtpred = dtn + ddtn * dtn = dtn + (dtn - dtn-1)
      */
      dtpred_old = dtpred;
      outpc.spare1 = (int)(dt3 - dtpred_old);
      dtpred = dt3 + dt3 - dt2;  // pred new time interval - time bet now and next plse                                                                                                                                                                                                                                    // predict next time interval
    }
  }
  else  {					// < 3 pulses received
    dtpred_old = dtpred;
    dtpred = dt3;
  }
  if(pulse_no < inpram.no_skip_pulses)  {  // skip 1st few (>1) pulses
	  pulse_no++;
	  // clear IC interrupt flag
	  TFLG1 = 0x01;
	  return;
  }
  else  {
    if(pulse_no < 3)
      pulse_no++;
  }
  // calculate rpm halfway between now and next ign pulse
  if(dtpred > 100)
    rpm_pred = (unsigned int)(Rpm_Coeff / dtpred);
  else  {
  	// Noise or beyond rev limit, clear all in engine
  	PTM &= ~0x01;    	// Turn off fuel Pump
  	PTM &= ~0x02;    	// Turn off fast idle ** Bug Fix By Guy Hill **
  	ign_reset();
  	return;
  }
  rpm = (unsigned int)(Rpm_Coeff / dt3);

AVE:
// rolling average
  if(inpram.RpmAveno <= 1)  {   // no averaging
    outpc.rpm = rpm;
  } 
  else if(rpm_avestate == 0)  {  // buffer not filled yet
    rpm_bufsum += rpm;
    rpm_buf[rpm_aveix] = rpm;
    rpm_aveix++;
    outpc.rpm = (unsigned int)(rpm_bufsum / rpm_aveix);
    if(rpm_aveix >= inpram.RpmAveno)  {
      rpm_avestate = 1;
      rpm_aveix = 0;
    }
  }
  else if(rpm_avestate == 1)  {	 
    // buffer filled - add new value remove old
    rpm_bufsum = rpm_bufsum - rpm_buf[rpm_aveix] + rpm;
    rpm_buf[rpm_aveix] = rpm;
    rpm_aveix++;
    outpc.rpm =(unsigned int)(rpm_bufsum / inpram.RpmAveno);
    if(rpm_aveix >= inpram.RpmAveno)  {
      rpm_aveix = 0;
    }
  }
  
// calculate ignition parameters
if(trig_ret_mode == 2)  {
/* Example tach signal used in trigger return mode                         
//  n-2         n-1          n          n+1         n+2
//     lead edge(70 btdc)    TDC      trail edge(5 btdc)
//      \/                    |         \/
//---    --------    --------    --------    --------
//   |  |        |  |        |  |        |  |        |
//   |__|        |__|        |__|        |__|        |_
//  /\          /\          /\  /\     /\   /\			 
// IC & Spk   IC&Spk     IC&Spk IC     Spk  IC
//               |----dt-----|  |----dt-----|
//      Cranking------->|<--------Not Cranking              
//                            /\
//                     transition(trig_ret_mode=1)
*/
  // Force spark NOW
	TCTL1 = (TCTL1 & 0xFB) | (SPK << 2);  // set OC o/p in OL5
	CFORC |= 0x20;           // force output in OL5 onto OC pin
	IgnOCpinstate = SPK;
  if(outpc.rpm > CRANK_RPM)  {    // finished cranking
    // transition to other edge for future ICs and out of trig ret mode
    if((TCTL4 & 0x03) == 0x01)
      TCTL4 = (TCTL4 & 0xFC) | 0x02;
    else if((TCTL4 & 0x03) == 0x02)
      TCTL4 = (TCTL4 & 0xFC) | 0x01;
    trig_ret_mode = 1;
    adv_us = 0;
    // don't want to do anything else til next IC on opposite edge,
    // then will set up to charge & spark in normal way, so disable
    // Ign OC interrupt.
    TIE &= ~0x20;
    goto SET_INJ;
  }
  else  {
    adv_us = 0;
    coil_dur_calc = (coil_dur * 100) << 1;  // us,  double to be sure coil 
            // sufficiently charged before next, possibly erratic IC
    charge_time = dtpred - adv_us - coil_dur_calc;   // us
    // want to start charging coil_dur_calc usec before next predicted IC,
    //   but don't want to discharge til next IC, so will disable OC
    //   when start charging.
  }
  tcnt = TCNT - TC0;   // TC0 is latched time at entry to ISR
  if(tcnt < 0)tcnt += 65535;
  tcnt = TC0 + tcnt + 90;  // add some extra time for final reg setting
  IgnTimerComp = TC0 + ((3*charge_time)>>1);     // 2/3 us
  if(IgnTimerComp < tcnt)
    IgnTimerComp = tcnt;  
  ign_state = 0;
  ign_setpin = CHG;  // use to set OC o/p in OL5
  goto CHECK_OVFLO;
}

if((inpram.ICIgnOption >> 4) < 1)  {
// Normal Ignition Output
// cyl n                                   cyl n+1
// tdc                                        tdc
//  |--------------- dtpred -------------------| 
//  |                __________                |
//  |               |          |               |
//  |               |-coil_dur-|----adv_us-----|
//  |--charge_time--|          |               |
//  |_______________|          |_______________|
// TC0                        Spk
//
  adv_us = ((long)16667 * ic_adv_deg) / rpm_pred;
  // The following limits max adv to 60 deg at 10krpm for 8 cyl and
  //  much more for < rpm, < no. cyl. It provides enough calculation
  //  time to do a prediction before the spark event.
  if(adv_us > (long)(dtpred - 500))
	  adv_us = (long)(dtpred - 500);
  // calculate time to start charging coil
  coil_dur_calc = coil_dur * 100;   // us
  ltmp1 = coil_dur_calc + (inpram.max_spk_dur * 100);
  if(ltmp1 > dtpred)  {
	  // scale down chg/ dischg time to fit time available
	  coil_dur_calc = (coil_dur_calc * dtpred) / ltmp1;
  }	
  charge_time = (long)(dtpred - adv_us - coil_dur_calc);   // us
  ltmp1 = dtpred - (dtpred >> 4);
  IgnOCpinstate = (PTIT & 0x20) >> 5;   // present pin state
  old_charge = 0;
  if(charge_time > (long)ltmp1)  {
     // charge time is > 6% of next IC; due to normal fluctuation
     // this could result in starting charge after IC.
     if(charge_time > dtpred)  {
       // In this case switch to 'this cylinder' mode (spark on 
       // this cylinder)
       adv_us += dtpred;
       if(IgnOCpinstate == SPK)  {
         charge_time -= dtpred;
       }
       else  {
         ign_state = 2;
       }
     }
     else  {
       if(IgnOCpinstate == CHG)  {
         // extend coil dur to ensure we start charging before dtpred
         coil_dur_calc += charge_time - ltmp1;
         charge_time = ltmp1;
         ign_state = 0;
       }
       else  {
         old_charge = charge_time;
         charge_time = 0;
         adv_us = dtpred + adv_us;
       }
     }
  }
} else  {
// EDIS Ignition Output
// cyl n                                   cyl n+1
// tdc                                        tdc
//  |-------- dtpred ---------------------------| 
//  |-delay- __________                         |
//  |       |          |                        |
//  |       |---SAW----|                        |
//  |       |          |                        |
//  |_______|          |________________________|
// TC0                        
//
  adv_us = 0;
  // send SAW pulse after delay, so we don't send
  // SAW while still sparking (send at 64us atdc)
  charge_time = 64;   //us
  // SAW pulse calculation
  ltmp1 = 1536 - ((long)256 * ic_adv_deg) /  100;
  // multispk EDIS
  if((inpram.ICIgnOption >> 4) == 2)  {
    if(first_edis)  {		 // 1st SAW for multispk is 2048 us
      first_edis = 0;
      ltmp1 = 2048;      // 1024 if ever have 10 cyl
    }
    else if(outpc.rpm < 1200)  {      
      ltmp1 += 2048;     // 1024 if ever have 10 cyl
    }
  }
  coil_dur_calc = ltmp1;
  IgnOCpinstate = SPK;
  tcnt = TCNT - TC0;   // TC0 is latched time at entry to ISR
  if(tcnt < 0)tcnt += 65535;
  tcnt = TC0 + tcnt + 90;  // add some extra time for final reg setting
  IgnTimerComp = TC0 + ((3*charge_time)>>1);     // 2/3 us
  if(IgnTimerComp < tcnt)
    IgnTimerComp = tcnt;  
  ign_state = 0;
  ign_setpin = CHG;  // use to set OC o/p in OL5
  goto CHECK_OVFLO;
}
 
 //  Only get here for normal ignition output
 if(IgnOCpinstate == SPK)  {
	tcnt = TCNT - TC0;   // TC0 is latched time at entry to ISR
	if(tcnt < 0)tcnt += 65535;
	tcnt = TC0 + tcnt + 20; // add extra time for final reg setting
	IgnTimerComp = TC0 + ((3*charge_time)>>1);     // 2/3 us
	if(IgnTimerComp < tcnt)  {
		// transitioning to early chge (large adv) state
		ign_state = 2;
		// Charge NOW
		TCTL1 = (TCTL1 & 0xFB) | (CHG << 2);  // set OC o/p in OL5
		CFORC |= 0x20;           // force output in OL5 onto OC pin
		IgnOCpinstate = CHG;
		// Set time to spark
		ltmp1 = (dtpred - adv_us);
		IgnTimerComp = TC0 + ((3*ltmp1)>>1);   // 2/3 us
		ign_setpin = SPK;  // use to set OC o/p in OL5
		if(old_charge > 0)  {
		  charge_time = old_charge - ltmp1;
		}
		else  {
		  // next chge time - predicted to be prior to next pulse input                                                                                 
		  charge_time += adv_us;     // rel to spk event
		}
	}
	else  {  // normal state - wait til time to charge
		ign_state = 0;
		ign_setpin = CHG;  // use to set OC o/p in OL5
	}
 }
 else  {  // OC output is charging 
	if(charge_time <= 0)  {		// => early chge, OC interrupt enabled
		// in early chge (large adv) state - stay there
		ign_state = 2;
		// Reset OC reg to update to latest spk time
		IgnTimerComp = TC0 + ((3*(dtpred - adv_us))>>1);   // 2/3 us
		// next chge time
		charge_time += adv_us;   // rel to spk event
		ign_setpin = SPK;
	}
	else  {
		if(ign_state == 2)  {
		  // transitioning to normal charge state
		  ign_state = 0;
		  // Reset OC reg to update to latest spk time
		  IgnTimerComp = TC0 + ((3*(dtpred - adv_us))>>1);   // 2/3 us
		  ign_setpin = SPK;
		}
		else  {
		  if(ic_adv_deg >= 0)  {
		    // IC came earlier than predicted and haven't sparked yet.
	FORCE_SPK:	    
		    // Force spark NOW
		    TCTL1 = (TCTL1 & 0xFB) | (SPK << 2);  // set OC o/p in OL5
		    CFORC |= 0x20;           // force output in OL5 onto OC pin
		    IgnOCpinstate = SPK;
		    IgnTimerComp = TC0 + ((3*charge_time)>>1);     // 2/3 us
		    ign_setpin = CHG;
		    ign_state = 0;
		  }
		  else  {
		    //  desired advance is atdc - update register to latest spk time
		    IgnTimerComp = TC0 - ((3 * adv_us)>>1);   // 2/3 us
		    tcnt = TCNT - TC0;   // TC0 is latched time at entry to ISR
		    if(tcnt < 0)tcnt += 65535;
		    tcnt = TC0 + tcnt + 90;	 // add some extra time to exit isr
		    if(IgnTimerComp < tcnt) goto FORCE_SPK;
		    charge_time += adv_us;   // rel to spk event
		    ign_setpin = SPK;
		    ign_state = 1;
		  }
		}
	}
 }

  // check if v. short OC reg occurs after TCNT (=free running, common
  //   timer).
  tcnt = TCNT - TC0;   // TC0 is latched time at entry to ISR
  if(tcnt < 0)tcnt += 65535;
  tcnt = TC0 + tcnt + 90;	 // add some extra time to get out of isr
  if(IgnTimerComp < tcnt)  {
    if(ign_setpin == CHG)  {
      // make spk time in OC come out exact
      ltmp1 = ((tcnt - IgnTimerComp)<<1)/3;         // us
      coil_dur_calc -= ltmp1;
      if(coil_dur_calc < 2)coil_dur_calc = 2;
    }
    IgnTimerComp = tcnt;  
  }
  
CHECK_OVFLO:
  coil_dur_set = coil_dur_calc;  
  if(IgnTimerComp > (long)65535)  {
    IgnOCt_overflow = IgnTimerComp >> 16;
    IgnTimerComp &= 0xFFFF;
  }
  else
    IgnOCt_overflow = 0;
  if(!IgnOCt_overflow)  {
    TC5 = (unsigned short)IgnTimerComp;  // load OC compare register
    // Set Ign OC pin & enable interrupt
    TCTL1 = (TCTL1 & 0xFB) | (ign_setpin << 2);
    TIE |= 0x20;
    TFLG1 = 0x20;	 // clear ign OC interrupt flag
  }
  else  {  // Disable Ign OC interrupt until ovflow done
    TIE &= ~0x20;
  }
  
SET_INJ:  
  //
  // Set up for Injector squirt(s)
  if(igncount == 0)
  	asecount++;
  egocount++;

  // Turn on fuel pump
  PTM |= 0x01;
  outpc.engine |= 0x01;   // set engine running

  if(outpc.engine & 0x02)goto SCHED_SQUIRT;  // if engine cranking
  igncount++;
  if(igncount < inpram.Divider)
    goto IC_EXIT;				  // skip Divider tach pulses 

SCHED_SQUIRT:
  igncount = 0;
  if(inpram.RevLimOption == 2)  {
    if(outpc.rpm > inpram.RevLimRpm2)
      // Cut fuel for Over Rev
	    cut_fuel = 1;
    else if(outpc.rpm < inpram.RevLimRpm1)
      // restore fuel
      cut_fuel = 0;
    if(cut_fuel)
	    goto IC_EXIT;
  }
  if((outpc.engine & 0x02) || !inpram.Alternate)  { // if cranking or no alternate option
  	sched_both = 1;
  	goto SCHED1;
  }
  sched_both = 0;
  altcount = 1 - altcount;
  if(altcount)
  	goto SCHED2;

SCHED1:
  // Turn On Inj1
  TCTL2 |= 0x04;   // set output hi in OL1
  CFORC |= 0x02;   // force high
  PWMPER2 = inpram.InjPWMPd;   // set PWM period (us)
  PWMDTY2 = PWMPER2;           // set PWM duty to 100 %
  pwm1_on = 0;
  pwm1_time = lmms + ((inpram.InjPWMTim*8)/10); // .128 ms
  PWMCNT2 = 0x00;  // clear counter
  PWME |= 0x04;    // enable PWM2
  // Set up to turn Off Inj1 when get to pw us
  outpc.pw1 = pwcalc1;
  Fl1TimerComp = TCNT + ((3*(long)outpc.pw1)>>1);  // 2/3 us
  Fl1OCt_overflow = (unsigned short)(Fl1TimerComp >> 16); 
  TC1 = (unsigned short)(Fl1TimerComp & 0xFFFF); // load OC compare reg
  if(Fl1OCt_overflow <= 1)  {
    // don't need to handle <= 1 overflow because TCNT will not
    // = TimerComp until after the overflow as long as pw < ~43 ms
		Fl1OCt_overflow = 0;
    TCTL2 = TCTL2 & 0xFB;    // set output lo in OL1
    TIE |= 0x02;             // Enable Inj1 OC interrupt
    TFLG1 = 0x02;	           // clear OC interrupt flag
  }
  else  {  // Disable Inj1 OC interrupt until ovflows done
		// do below to allow enough time to set OC pin in overflow isr
		if(TC1 < 30)TC1 = 30;   
  	TIE &= ~0x02;
  }
  PTM |= 0x08;     // turn on inj led
  outpc.squirt |= 0x01;  // inj1 squirting
  if(!sched_both)
  	goto IC_EXIT;

SCHED2:
  // Turn On Inj2
  TCTL2 |= 0x40;   // set output hi in OL3
  CFORC |= 0x08;   // force high
  PWMPER4 = inpram.InjPWMPd;   // set PWM period (us)
  PWMDTY4 = PWMPER4;           // set PWM duty to 100 %  
  pwm2_on = 0;
  pwm2_time = lmms + ((inpram.InjPWMTim*8)/10); // .128 ms
  PWMCNT4 = 0x00;  // clear counter
  PWME |= 0x10;    // enable PWM4
  // Set up to turn Off Inj2 when get to pw us
  outpc.pw2 = pwcalc2;
  Fl2TimerComp = TCNT + ((3*(long)outpc.pw2)>>1);  // 2/3 us
  Fl2OCt_overflow = (unsigned short)(Fl2TimerComp >> 16); 
  TC3 = (unsigned short)(Fl2TimerComp  & 0xFFFF); // load OC compare reg
  if(Fl2OCt_overflow <= 1)  {
    // don't need to handle <= 1 overflow because TCNT will not
    // = TimerComp until after the overflow as long as pw < ~43 ms
    Fl2OCt_overflow = 0;
    TCTL2 = TCTL2 & 0xBF;   // set output lo in OL3
    TIE |= 0x08;            // Enable Inj2 OC interrupt
    TFLG1 = 0x08;	          // clear OC interrupt flag
  }
  else  {  // Disable Inj2 OC interrupt until ovflows done
		// do below to allow enough time to set OC pin in overflow isr
		if(TC3 < 30)TC3 = 30;   
    TIE &= ~0x08;
  }
  PTM |= 0x08;     // turn on inj led
  outpc.squirt |= 0x02;  // inj2 squirting

IC_EXIT:
  // Set up to renable IC interrupt in Timer ISR at 1/2 way
  //  to next IC to avoid coil ringing noise false interrupts
  t_enable_IC = ltch_lmms + ((dtpred * 8) / 2000);	// .128 ms units
  TIE &= ~0x01;   // disable interrupt
  TFLG1 = 0x01;		// clear flag
  return;		
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        

#pragma CODE_SEG NON_BANKED        
#endif
INTERRUPT void ISR_TimerOverflow(void)  {
  // Get display seconds from continuously running TCNT
  TC_ovflow++;
  if(TC_ovflow >= TC_ovfla[TC_ov_ix])  {
    // update seconds to send back to PC
    outpc.seconds++;
    if(TC_ov_ix >= 59)  {
      TC_ov_ix = 0;
      TC_ovflow = 0;
    }
    else
      TC_ov_ix++;
  }

  // Handle Ign IC stuff
  if(pulse_no == 0)  {
  	// clear Timer Overflow interrupt flag (TOF)
  	TFLG2 = 0x80;
  	return;
  }
  else  {
  	ICt_overflow++;
  }

  // Handle Ign OC stuff
  // Example:  TC0 = 30K us,  dt = 80K, TC0 + dt = 110k
  //    OC reg = 110K - 65K = 45K  => overflow = 1
  //    Timer counter goes from 30K to 65K/ 0 to 45K/ 1
  //    elapsed time = (65 - 30 = 35K) + 45K = 80K = dt
  if(IgnOCt_overflow > 0)  {
    IgnOCt_overflow--;
    if(IgnOCt_overflow == 0)  {
		  // check if v. short OC reg occurs after TCNT (=free running,
		  //   common timer). Enter this isr with TCNT=0, so if needed,
		  //   add some extra time to get out of isr before OC time reached
		  if(IgnTimerComp < TCNT + 18)
		    IgnTimerComp = TCNT + 18;  
		  TC5 = (unsigned short)IgnTimerComp;  // load OC compare register
		  // set/ enable ign OC pin and interrupt
		  TCTL1 = (TCTL1 & 0xFB) | (ign_setpin << 2);
		  TIE |= 0x20;
		  TFLG1 = 0x20;	 // clear ign OC interrupt flag
    }
  }

  // Handle Inj OC stuff
  if(Fl1OCt_overflow > 0)  {
    Fl1OCt_overflow--;
    if(Fl1OCt_overflow == 0)  {
  	  // set/ enable inj1 OC pin and interrupt
      TCTL2 = TCTL2 & 0xFB;      // set output lo in OL1
  	  TIE |= 0x02;
      TFLG1 = 0x02;	 // clear OC interrupt flag
    }
  }
  if(Fl2OCt_overflow > 0)  {
    Fl2OCt_overflow--;
    if(Fl2OCt_overflow == 0)  {
  	  // set/ enable inj2 OC pin and interrupt
      TCTL2 = TCTL2 & 0xBF;      // set output lo in OL3
  	  TIE |= 0x08;
      TFLG1 = 0x08;	 // clear OC interrupt flag
    }
  }

  // clear timer overflow interrupt flag (TOF)
  TFLG2 = 0x80;
  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        

#pragma CODE_SEG NON_BANKED        
#endif
INTERRUPT void ISR_Ign_TimerOut(void)  {
long tcnt;

// Enter here when ign OC reg match
  IgnOCpinstate = (TCTL1 & 0x04) >> 2;
  if(IgnOCpinstate == CHG)  {
  	// Coil has just started charging
  	if(trig_ret_mode)  {
  		// disable ign OC interrupt - will spk on next IC
  		TIE &= ~0x20;
  		// clear ign OC interrupt flag
  		TFLG1 = 0x20;
  		return;
  	}
  	IgnTimerComp = TC5 + ((3*coil_dur_set)>>1);  // 2/3 us
  	ign_setpin = SPK;
  }
  else  {
  	// Coil has just started discharging (sparking)
	  if(ign_state == 0)  {
  		// disable ign OC interrupt - chge after next IC pulse
  		TIE &= ~0x20;
  		// clear ign OC interrupt flag
  		TFLG1 = 0x20;
  		return;
	  }
	  else  {  // want to charge after done with spark
  		IgnTimerComp = TC5 + ((3*charge_time)>>1);  // 2/3 us
	  }
	  ign_setpin = CHG;  // use to set OC o/p in OL5
  }
  // check if v. short OC reg occurs after TCNT
  tcnt = TCNT - TC5;   // TC5 is OC time at entry to ISR
  if(tcnt < 0)tcnt += 65535;
  tcnt = TC5 + tcnt + 12;
  if(IgnTimerComp < tcnt)IgnTimerComp = tcnt;
  if(IgnTimerComp > (long)65535)  {
    IgnOCt_overflow = IgnTimerComp >> 16;
    IgnTimerComp &= 0xFFFF;
  }
  else
    IgnOCt_overflow = 0;
  if(!IgnOCt_overflow)  {     
    TC5 = (unsigned short)IgnTimerComp;  // load ign OC register
    // enable Ign OC pin
    TCTL1 = (TCTL1 & 0xFB) | (ign_setpin << 2);
    // clear ign OC interrupt flag
    TFLG1 = 0x20;
  }
  else  {     // Disable Ign OC intrpt til ovflow done
  	TIE &= ~0x20;
  }

  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        

#pragma CODE_SEG NON_BANKED        
#endif
INTERRUPT void ISR_Inj1_TimerOut(void)  {

// Enter here when OC match to cease firing Inj1.
// At entry here the pin will automatically turn off injector;
  // also turn off pwm1 (this not necessary since nanded)
  PWME &= ~0x04;
  outpc.squirt &= ~0x01; // inj1 stopped squirting
  if(!(outpc.squirt & 0x03))	 // neither inj squirting
    PTM &= ~0x08;    // turn off inj led
  // Disable Inj1 OC interrupt - will re-enable on next tach pulse
  TIE &= ~0x02;
  // clear OC interrupt flag
  TFLG1 = 0x02;
  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        

#pragma CODE_SEG NON_BANKED        
#endif
INTERRUPT void ISR_Inj2_TimerOut(void)  {

// Enter here when OC match to cease firing Inj2.
// At entry here the pin will automatically turn off injector;
  // also turn off pwm2 (this not necessary since nanded)
  PWME &= ~0x10;
  outpc.squirt &= ~0x02; // inj2 stopped squirting
  if(!(outpc.squirt & 0x03))	 // neither inj squirting
    PTM &= ~0x08;    // turn off inj led
  // Disable Inj2 OC interrupt - will re-enable on next tach pulse
  TIE &= ~0x08;
  // clear OC interrupt flag
  TFLG1 = 0x08;
  return;
}
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        
#endif

int move_IACmotor(void)  {
unsigned char coils;
  if(IAC_moving || (outpc.iacstep == IACmotor_pos))return 0;
  // set up the motor move
  motor_time = lmms + ((inpram.IACtstep*8)/10);  // .128 ms
  motor_step++;
  if(motor_step > 7)motor_step = 0;
  if(IACmotor_pos > outpc.iacstep)  {    // cw
  	coils = (IACCoilA[7-motor_step] << 6) |
  	  (IACCoilB[7-motor_step] << 7);
  }
  else  {                                // ccw
  	coils = (IACCoilA[motor_step] << 6) | 
  	  (IACCoilB[motor_step] << 7);
  }
  if(inpram.IdleCtl == 2)  {
    PORTB &= ~0x10;  // enable current to motor(bit=0)
  }
  DISABLE_INTERRUPTS
  PTT = (PTT &0x3F) | coils;
  ENABLE_INTERRUPTS
  IAC_moving = 2;
  return 1;
}


/**************************************************************************
**
** SCI Communications
**
** Communications is established when the PC communications program sends
** a command character - the particular character sets the mode:
**
** "a" = send all of the realtime display variables (outpc structure) via txport.
** "v" = send all input parameters (inpram structure) via txport.
** "w"+<offset lsb>+<offset msb>+<nobytes>+<newbytes> = 
**    receive an updated input parameter and store in offset location
**    relative to start of inpram structure
** "W" = same as "w" above, followed by "r" below
** "r"+<offset lsb>+<offset msb>+<nobytes>+<newbytes> =
**    send back value of input parameter in offset location
** "B" = jump to flash burner routine and burn all input parameter values in 
**    RAM (inpram structure) into flash (inpflash structure).
** "C" = Test communications - echo back Seconds
** "Q" = Send over Embedded Code Revision Number (divide number by 10 - i.e.
**  $21T is rev 2.1)
**
**************************************************************************/
#ifndef GCC_BUILD
#pragma CODE_SEG  NON_BANKED
#endif
INTERRUPT void ISR_SCI_Comm(void)  {
  char dummy;
  int ix;
  static int vfy_fail,rd_wr;
  
  // if RDRF register not set, => transmit interrupt
  if(!(SCI0SR1 & 0x20))goto XMT_INT;

// Receive Interrupt
  // Clear the RDRF bit by reading SCISR1 register (done above), then read data
  //  (in SCIDRL reg).
  // Check if we are receiving new input parameter update
txgoal = 0;
rcv_timeout = 0xFFFFFFFF;
switch(txmode)  {

case 0:

  switch(SCI0DRL)  {
    case 'a':
  txcnt = 0;         // send back all real time variables
  txmode = 1;
  txgoal = sizeof(outpc);
  // load all output variables into txbuf to avoid updates
  memcpy(&txbuf,&outpc,sizeof(outpc));
  SCI0DRL = *(char *)&txbuf;
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;

    case 'v':         // send back all user inputs as they exist in RAM
  txcnt = 0;
  txmode = 3;
  txgoal = sizeof(inpram);
  SCI0DRL = *(char *)&inpram;   
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;

    case 'w':		  // receive new user input and store in offset location
  txmode = 5;
  rd_wr = 1;        
  break;

    case 'W':		  // same as 'w', but verify by sending back value
  txmode = 5;
  rd_wr = 2;        
  break;

    case 'r':		  // read back input data in offset location
  txmode = 5;
  rd_wr = 0;        
  break;

    case 'y':         // Verify that inpflash matches inpram
  vfy_fail = 0;
  for(ix = 0; ix < sizeof(inpram); ix++)  {
    if(*((unsigned char *)&inpflash + ix) != *((unsigned char *)&inpram + ix))
      vfy_fail++;
  }
  txcnt = 0;
  txmode = 2;
  txgoal = 2;
  SCI0DRL = *(char *)&vfy_fail;   
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;
   	
    case 'b':        // burn all existing ram input values into flash
  flocker = 0xCC;    // set semaphore to prevent burning flash thru runaway code
  burn_flag =1;
  txmode = 0;
  break;

    case 't':        // update a flash table with following serial data
  txmode = 20;
  break;

    case '!':        // start receiving reinit/reboot command
  txmode = 30;
  break;

    case 'c':        // send back seconds to test comms
  txcnt = 0;         
  txmode = 1;
  txgoal = 2;        // seconds is 1st 2 bytes of outpc structure
  txbuf.seconds = outpc.seconds;
  SCI0DRL = *(char *)&txbuf;
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;

    case 'Q':         // send code rev no.
  txcnt = 0;
  txmode = 4;
  txgoal = 20;
  SCI0DRL = RevNum[0]; 
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;

    case 'S':         // send program title
  txcnt = 0;
  txmode = 5;
  txgoal = 32;
  SCI0DRL = Signature[0]; 
  SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
  break;

    default:
  break;
  }     // End of switch for received command
    break;

case 5:
  	rxoffset = (SCI0DRL << 8); // byte offset(msb) from start of inpram
  	txmode++;
  	break;

case 6:
  	rxoffset |= SCI0DRL;    // byte offset(lsb) from start of inpram
  	txmode++;
  	break;
  	
case 7:
  	rxnbytes = (SCI0DRL << 8); 	// no. bytes (msb)
  	txmode++;
  	break;

case 8:
  	rxnbytes |= SCI0DRL;		// no. bytes (lsb)
  	rxcnt = 0;
  	if(rd_wr == 0)  {		    // read & send back data from inpram
      txcnt = 0;
      txmode = 3;
      txgoal = rxnbytes;
      SCI0DRL = *((char *)&inpram  + rxoffset);   
      SCI0CR2 |= 0x88;        // xmit enable & xmit interrupt enable
  	}
  	else  {                   // write data to inpram
  	  txmode++;
  	}
  	break;

case 9:
  	// Check for data overrun.  Note: this
  	// still not bullet proof because could write half the bytes
  	// then have overrun; need to store and write all at once if
  	// all is valid.
  	if((SCI0SR1 & 0x08) == 0) 	 // none yet- write data.
  	  *((char *)&inpram + (rxoffset + rxcnt)) = SCI0DRL;
  	rxcnt++;
  	if(rxcnt >= rxnbytes)  {
      dummy = SCI0DRL;      // clear OR bit (read sci0sr1(done), sci0drl)
      if(rd_wr == 1)
  		txmode = 0;         // done receiving all the data
      else  {
        txcnt = 0;				// send back inpram data just written
        txmode = 3;				//	 for verification
        txgoal = rxnbytes;
        SCI0DRL = *((char *)&inpram  + rxoffset);   
        SCI0CR2 |= 0x88;    // xmit enable & xmit interrupt enable
      }
  	}
  	break;

case 20:	 // d/load & burn tables - *** Do while engine NOT turning ***
  	tble_type = SCI0DRL;    // type of table
  	ntword = 0;
    flocker = 0xCC; // set semaphore to prevent burning flash thru runaway code
  	tburner(0,tble_addr[tble_type],no_tble_words[tble_type]);   // erase table
  	flocker = 0;
  	txmode++;
  	break;

case 21:
  	tble_word = (SCI0DRL << 8);    // msb
  	txmode++;
  	break;

case 22:
  tble_word |= SCI0DRL;            // lsb
  flocker = 0xCC; // set semaphore to prevent burning flash thru runaway code
  tburner(1,tble_addr[tble_type] + ntword,tble_word);   // write table word
  flocker = 0;
  ntword++;
  if(ntword < no_tble_words[tble_type])
    txmode = 21;
  else  {
    txmode = 0;
    ign_reset();
  }
  break;

case 30:
  	if(SCI0DRL == '!')    // receive 2nd ! for reboot signal
  	  txmode++;
  	else if(SCI0DRL == 'x')  {  // received reinit signal (!x)
  	  reinit_flag = 1;
  	  txmode = 0;
  	}
  	else
  	  txmode = 0;
  	break;

case 31:
  	if(SCI0DRL == 'x')  {   // received complete reboot signal(!!x)
  	  if(outpc.rpm < CRANK_RPM)  {  // check not rebooting at hi rpm
  	    // go to start of bootload pgm
  	    //asm (jmp $F800);
  	    (void)reboot();
  	  }
  	}
  	else if(SCI0DRL == '!')  {   // received complete reload signal(!!!)
  	  if(outpc.rpm < CRANK_RPM)  { // check not reloading code at hi rpm
  	    SCI0CR1 = 0x00;
  	    SCI0CR2 = 0x00;      // disable Tx, Rx
  	    // go to start of monitor code
  	    //asm (jmp $F842);
  	    (void)monitor();
  	  }
  	}
  	txmode = 0;
  	break;

default:
  dummy = SCI0DRL;   // dummy read of reg to clear int flag
  break;

  }     // End of case switch for received data
    
  if((txgoal == 0) && (txmode > 0))
    rcv_timeout = lmms + 3906;   // 0.5 sec timeout
  return;

// Transmit Interrupt
XMT_INT:
  // Clear the TDRE bit by reading SCISR1 register (done), then write data
  txcnt++;
  if(txcnt < txgoal)  {
    if(txmode == 3)  {
  	  SCI0DRL = *((char *)&inpram + txcnt);
    } 
    else if(txmode == 4)  {
  	  SCI0DRL = RevNum[txcnt];
    }
    else if(txmode == 5)  {
  	  SCI0DRL = Signature[txcnt];
    }
    else if(txmode == 2)  {
  	  SCI0DRL = *((char *)&vfy_fail + txcnt);
    } 
    else  {
      SCI0DRL = *((char *)&txbuf + txcnt);
    }
  } 
  else  {			 // done transmitting
    txcnt = 0;
    txgoal = 0;
    txmode = 0;
    SCI0CR2 &= ~0x88;    // xmit disable & xmit interrupt disable
  } 
  return;
}														 
#ifndef GCC_BUILD
#pragma CODE_SEG DEFAULT        
#endif

int ign_table(unsigned int rpm, int map)  {
  int ix,jx;
  long interp1, interp2, interp3;
  // returns advance in deg x 10
  // bound input arguments
  if(rpm > inpram.srpm_table[NO_SRPMS-1])rpm = inpram.srpm_table[NO_SRPMS-1];
  else if(rpm < inpram.srpm_table[0])rpm = inpram.srpm_table[0];
  if(map > inpram.smap_table[NO_SMAPS-1])map = inpram.smap_table[NO_SMAPS-1];
  else if(map < inpram.smap_table[0])map = inpram.smap_table[0];
  // Look up spark advance in table
  for(ix = NO_SMAPS - 2; ix > -1; ix--)  {  // Start w highest index
	//  because will have least time for calculations at hi maps
  	if(map > inpram.smap_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
  for(jx = NO_SRPMS - 2; jx > -1; jx--)  {  // Start w highest index
	// because will have least time for calculations at hi rpms
	  if(rpm > inpram.srpm_table[jx])  {
	    break;
    }
  }
  if(jx < 0)jx = 0;

  interp1 = inpram.smap_table[ix + 1] - inpram.smap_table[ix];
  if(interp1 != 0)  {
    interp3 = (map - inpram.smap_table[ix]); 
    interp3 = (100 * interp3); 
    interp1 = interp3 / interp1; 
  }
  interp2 = inpram.srpm_table[jx + 1] - inpram.srpm_table[jx];
  if(interp2 != 0)  {
    interp3 = (rpm - inpram.srpm_table[jx]); 
    interp3 = (100 * interp3); 
    interp2 = interp3 / interp2; 
  }
  return((int)(((100 - interp1) * (100 - interp2) * inpram.adv_table[ix][jx]
	  + interp1 * (100 - interp2) * inpram.adv_table[ix+1][jx]
	  + interp2 * (100 - interp1) * inpram.adv_table[ix][jx+1]
	  + interp1 * interp2 * inpram.adv_table[ix+1][jx+1]) / 10000));
                            // deg adv in crank - deg x10 btdc
}

int cold_ign_table(int clt)  {
  int ix;
  long interp, interp3;
  // returns advance in deg x 10
  // bound input arguments
  if(clt > inpram.temp_table[NO_TEMPS-1])  {
    return((int)inpram.cold_adv_table[NO_TEMPS -1]);
  }
  if(clt < inpram.temp_table[0])  {
    return((int)inpram.cold_adv_table[0]);
  }
  // Look up spark advance in table
  for(ix = NO_TEMPS - 2; ix > -1; ix--)  {  // Start w highest index
	//  because that will most often be the case
  	if(clt > inpram.temp_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
 
  interp = inpram.temp_table[ix + 1] - inpram.temp_table[ix];
  if(interp != 0)	 {
    interp3 = (clt - inpram.temp_table[ix]);
    interp3 = (100 * interp3);
    interp = interp3 / interp;
  }
  return((int)(inpram.cold_adv_table[ix] +
	  interp * (inpram.cold_adv_table[ix+1] - inpram.cold_adv_table[ix])/ 100));
                            // deg adv in crank - deg x10 btdc
}

int barocor_table(int baro)  {
  // returns baro correction in % (100 is no correction)
   // baro in kPa x 10
   return((int)(inpram.bcor0 + (((long)inpram.bcormult * baro) / 1000)));
}

int aircor_table(int mat)  {
  // returns air density correction from equation in % (100 is no correction)
  //   mat in deg F x10 or deg Cx10
  /* Following equations used:

       For Farenheit, air density (pounds/cubic feet) = .0391568 x map (kPa x 10)
                                                        -------------------------
                                                       ((mat(degFx10)/10) + 459.7)
       If we take 70 degF as standard temperature (no density correction) and ignore
       map, which is already accounted for elsewhere,then
         air density correction(%) = (70 + 459.7) x 100
                                     ------------------
                                     ((mat/10) + 459.7)
       For Celsius, with a 20 deg C reference and mat in deg C x 10
         air density correction(%) = (20 + 273.2) x 100
                                     ------------------
                                     ((mat/10) + 273.2)
  */ 
  if(inpram.Temp_Units == 0)  {    // deg F
  	return((int)(529700 / (mat + 4597)));
  }
  else  {                          // deg C
  	return((int)(293200 / (mat + 2732)));
  }
}

int warmcor_table(int clt)  {
  int ix;
  long interp, interp3;
  // returns warmup correction from table lookup in % (100 is no correction)
  // bound input arguments
  if(clt > inpram.temp_table[NO_TEMPS-1])  {
    return((int)inpram.warmen_table[NO_TEMPS -1]);
  }
  if(clt < inpram.temp_table[0])  {
    return((int)inpram.warmen_table[0]);
  }
  for(ix = NO_TEMPS - 2; ix > -1; ix--)  { 
  	if(clt > inpram.temp_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
 
  interp =	inpram.temp_table[ix + 1] - inpram.temp_table[ix];
  if(interp != 0)  {
    interp3 = (clt - inpram.temp_table[ix]);
    interp3 = (100 * interp3);
    interp = interp3 / interp;
  }
  return((int)(inpram.warmen_table[ix] +
	  interp * (inpram.warmen_table[ix+1] - inpram.warmen_table[ix])/ 100));
}

int iaccmd_table(int clt)  {
  int ix;
  long interp, interp3;
  // returns IAC stepper motor commands from table lookup in steps as function
  // of clt. 0 steps is fully open (fast idle) and IACStart steps is fully closed
  // (slow idle).
  // bound input arguments
  if(clt > inpram.temp_table[NO_TEMPS-1])  {
    return((int)(inpram.IACScale *inpram.iacstep_table[NO_TEMPS -1]));
  }
  if(clt < inpram.temp_table[0])  {
    return((int)(inpram.IACScale *inpram.iacstep_table[0]));
  }
  for(ix = NO_TEMPS - 2; ix > -1; ix--)  { 
  	if(clt > inpram.temp_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
 
  interp = inpram.temp_table[ix + 1] - inpram.temp_table[ix];
  if(interp != 0)  {
    interp3 = (clt - inpram.temp_table[ix]);
    interp3 = (100 * interp3);
    interp = interp3 / interp;
  }
  return((int)(inpram.IACScale *(inpram.iacstep_table[ix] +
	  interp * (inpram.iacstep_table[ix+1] - inpram.iacstep_table[ix])/ 100)));
}

int tpscor_table(int tpsdot)  {
  int ix;
  long interp, interp3;
  // returns tps accel pw correction from table lookup in .1 ms units
  // bound input arguments
  if(tpsdot > inpram.tpsdot_table[NO_TPS_DOTS-1])  {
    return((int)inpram.tpsen_table[NO_TPS_DOTS -1]);
  }
  if(tpsdot < inpram.tpsdot_table[0])  {
    return((int)inpram.tpsen_table[0]);
  }
  // tpsdot in % x 10 change per .10035 sec
  for(ix = NO_TPS_DOTS - 2; ix > -1; ix--)  { 
  	if(tpsdot > inpram.tpsdot_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
 
  interp = inpram.tpsdot_table[ix + 1] - inpram.tpsdot_table[ix];
  if(interp != 0)  {
    interp3 = (tpsdot - inpram.tpsdot_table[ix]);
    interp3 = (100 * interp3);
    interp = interp3 / interp;
    if(interp < 0) interp = 0;
  }
  return((int)(inpram.tpsen_table[ix] +
	  interp * (inpram.tpsen_table[ix+1] - inpram.tpsen_table[ix])/ 100));
}

int mapcor_table(int mapdot)  {
  int ix;
  long interp, interp3;
  // returns map accel pw correction from table lookup in .1 ms units
  // bound input arguments
  if(mapdot > inpram.mapdot_table[NO_MAP_DOTS-1])  {
    return((int)inpram.mapen_table[NO_MAP_DOTS -1]);
  }
  if(mapdot < inpram.mapdot_table[0])  {
    return((int)inpram.mapen_table[0]);
  }
  // mapdot in kPa x 10 change per .10035 sec
  for(ix = NO_MAP_DOTS - 2; ix > -1; ix--)  { 
  	if(mapdot > inpram.mapdot_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
 
  interp = inpram.mapdot_table[ix + 1] - inpram.mapdot_table[ix];
  if(interp != 0)  {
    interp3 = (mapdot - inpram.mapdot_table[ix]);
    interp3 = (100 * interp3);
    interp = interp3 / interp;
    if(interp < 0)interp = 0;
  }
  return((int)(inpram.mapen_table[ix] +
	  interp * (inpram.mapen_table[ix+1] - inpram.mapen_table[ix])/ 100));
}

int vecalc_table(unsigned int rpm, int map, char inj)  {
  int ix,jx;
  long interp1, interp2, interp3;
  // returns VE in % x 10
  // bound input arguments
  if(rpm > inpram.frpm_table[NO_FRPMS-1])rpm = inpram.frpm_table[NO_FRPMS-1];
  else if(rpm < inpram.frpm_table[0])rpm = inpram.frpm_table[0];
  if(map > inpram.fmap_table[NO_FMAPS-1])map = inpram.fmap_table[NO_FMAPS-1];
  else if(map < inpram.fmap_table[0])map = inpram.fmap_table[0];
  // Look up VE in table
  for(ix = NO_FMAPS - 2; ix > -1; ix--)  {  // Start w highest index
	//  because will have least time for calculations at hi maps
  	if(map > inpram.fmap_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
  for(jx = NO_FRPMS - 2; jx > -1; jx--)  {  // Start w highest index
	// because will have least time for calculations at hi rpms
	  if(rpm > inpram.frpm_table[jx])  {
	    break;
    }
  }
  if(jx < 0)jx = 0;

  interp1 = inpram.fmap_table[ix + 1] - inpram.fmap_table[ix];
  if(interp1 != 0)  {
    interp3 = (map - inpram.fmap_table[ix]); 
    interp3 = (100 * interp3); 
    interp1 = interp3 / interp1; 
  }
  interp2 =	inpram.frpm_table[jx + 1] - inpram.frpm_table[jx];
  if(interp2 != 0)  {
    interp3 = (rpm - inpram.frpm_table[jx]); 
    interp3 = (100 * interp3); 
    interp2 = interp3 / interp2; 
  }
  return((int)(((100 - interp1) * (100 - interp2) * inpram.ve_table[(int)inj][ix][jx]
	  + interp1 * (100 - interp2) * inpram.ve_table[(int)inj][ix+1][jx]
	  + interp2 * (100 - interp1) * inpram.ve_table[(int)inj][ix][jx+1]
	  + interp1 * interp2 * inpram.ve_table[(int)inj][ix+1][jx+1]) / 10000));
                            // VE in % 
}

int afrcalc_table(unsigned int rpm, int map, char inj)  {
  int ix,jx;
  long interp1, interp2, interp3;
  // returns AFR x 10
  // bound input arguments
  if(rpm > inpram.frpm_table[NO_FRPMS-1])rpm = inpram.frpm_table[NO_FRPMS-1];
  else if(rpm < inpram.frpm_table[0])rpm = inpram.frpm_table[0];
  if(map > inpram.fmap_table[NO_FMAPS-1])map = inpram.fmap_table[NO_FMAPS-1];
  else if(map < inpram.fmap_table[0])map = inpram.fmap_table[0];
  // Look up AFR in table
  for(ix = NO_FMAPS - 2; ix > -1; ix--)  {  // Start w highest index
	//  because will have least time for calculations at hi maps
  	if(map > inpram.fmap_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
  for(jx = NO_FRPMS - 2; jx > -1; jx--)  {  // Start w highest index
	// because will have least time for calculations at hi rpms
	  if(rpm > inpram.frpm_table[jx])  {
	    break;
    }
  }
  if(jx < 0)jx = 0;

  interp1 = inpram.fmap_table[ix + 1] - inpram.fmap_table[ix] ;
  if(interp1 != 0)  {
    interp3 = (map - inpram.fmap_table[ix]); 
    interp3 = (100 * interp3); 
    interp1 = interp3 / interp1; 
  }
  interp2 = inpram.frpm_table[jx + 1] - inpram.frpm_table[jx];
  if(interp2 != 0)	 {
    interp3 = (rpm - inpram.frpm_table[jx]);
    interp3 = (100 * interp3);
    interp2 = interp3 / interp2;
  }
  return((int)(((100 - interp1) * (100 - interp2) * inpram.afr_table[(int)inj][ix][jx]
	  + interp1 * (100 - interp2) * inpram.afr_table[(int)inj][ix+1][jx]
	  + interp2 * (100 - interp1) * inpram.afr_table[(int)inj][ix][jx+1]
	  + interp1 * interp2 * inpram.afr_table[(int)inj][ix+1][jx+1]) / 10000));
                            // AFR x 10
}

int alphcalc_table(unsigned int rpm, int tps)  {
  int ix,jx;
  long interp1, interp2, interp3;
  // returns kPa x 10 for alpha-N option
  // bound input arguments
  if(rpm > inpram.amap_rpm[6-1])rpm = inpram.amap_rpm[6-1];
  else if(rpm < inpram.amap_rpm[0])rpm = inpram.amap_rpm[0];
  if(tps > inpram.amap_tps[6-1])tps = inpram.amap_tps[6-1];
  else if(tps < inpram.amap_tps[0])tps = inpram.amap_tps[0];
  // Look up tps in table
  for(ix = 6 - 2; ix > -1; ix--)  {  // Start w highest index
	//  because will have least time for calculations at hi tps
  	if(tps > inpram.amap_tps[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
  for(jx = 6 - 2; jx > -1; jx--)  {  // Start w highest index
	// because will have least time for calculations at hi rpms
	  if(rpm > inpram.amap_rpm[jx])  {
	    break;
    }
  }
  if(jx < 0)jx = 0;

  interp1 =	inpram.amap_tps[ix + 1] - inpram.amap_tps[ix];
  if(interp1 != 0)  {
    interp3 = (tps - inpram.amap_tps[ix]);
    interp3 = (100 * interp3);
    interp1 = interp3 / interp1;
  }
  interp2 = inpram.amap_rpm[jx + 1] - inpram.amap_rpm[jx];
  if(interp2 != 0)	{
    interp3 = (rpm - inpram.amap_rpm[jx]);
    interp3 = (100 * interp3);
    interp2 = interp3 / interp2;
  }
  return((int)(((100 - interp1) * (100 - interp2) * inpram.alpha_map_table[ix][jx]
	  + interp1 * (100 - interp2) * inpram.alpha_map_table[ix+1][jx]
	  + interp2 * (100 - interp1) * inpram.alpha_map_table[ix][jx+1]
	  + interp1 * interp2 * inpram.alpha_map_table[ix+1][jx+1]) / 10000));
                            // kpa x 10
}

int coil_dur_table(int delta_volt)  {
  int ix;
  long interp, interp3;
  // returns correction for coil duration vs battery voltage deviation
  //  from 12 V from table lookup in .1 ms units
  // bound input arguments
  if(delta_volt > inpram.deltV_table[NO_COILCHG_PTS-1])  {
    return((int)inpram.deltDur_table[NO_COILCHG_PTS-1]);
  }
  if(delta_volt < inpram.deltV_table[0])  {
    return((int)inpram.deltDur_table[0]);
  }
  // delta_volt in V x 10
  for(ix = NO_COILCHG_PTS - 2; ix > -1; ix--)  { 
  	if(delta_volt > inpram.deltV_table[ix])  {
   		break;
  	}
  }
  if(ix < 0)ix = 0;
 
  interp = inpram.deltV_table[ix + 1] - inpram.deltV_table[ix];
  if(interp != 0)  {
    interp3 = (delta_volt - inpram.deltV_table[ix]);
    interp3 = (100 * interp3);
    interp = interp3 / interp;
    if(interp < 0)interp = 0;
  }
  return((int)(inpram.deltDur_table[ix] +
	  interp * (inpram.deltDur_table[ix+1] - inpram.deltDur_table[ix])/ 100));
}

void set_spr_port(char port, char val)  {
     switch(port)  {
      case 0:
        if(val)
          PTM |= 0x04;      // PTM bit2 = 1
        else
          PTM &= ~0x04;     // PTM bit2 = 0
        break;
      case 1:
        if(val)
          PORTE |= 0x10;    // PTE bit4 = 1
        else
          PORTE &= ~0x10;   // PTE bit4 = 0
        break;
      default:
        break;
    }
}
#ifndef GCC_BUILD
#pragma CODE_SEG NON_BANKED 

__interrupt void UnimplementedISR(void) {
   /* Unimplemented ISRs trap.*/
   return;
}
#pragma CODE_SEG DEFAULT
#endif

/******************************************************************************
Arguments:	

      progAdr		Pointer to the start of the destination 
						                    Flash location to be programmed
                                    
			bufferPtr	Pointer to the start of the source data 
                                    
			size        Number of WORDS to be programmed
                  
		This function will program non-paged flash only 
******************************************************************************/
void fburner(unsigned int* progAdr, unsigned int* bufferPtr, 
                                          unsigned int no_words)
{
unsigned char sect,no_sectors;
																
  	if(flocker != 0xCC)return; // if not set, we got here unintentionally
  	// sector = 512 bytes (256 words)
  	if(burn_flag == 1)  {
  	  no_sectors = (no_words/256) + 1;
  	  for (sect = 0; sect < no_sectors; sect++)  {  	  
  	    Flash_Erase_Sector(progAdr + sect * 256);
  	  }
  	}
  	Flash_Write_Word(progAdr + burn_flag-1, *(bufferPtr + burn_flag-1));
    	
	return;
}

/*  Burn tables in flash one sector at time */
void tburner(char erase_write, unsigned int* addr, unsigned int wrd) 
{
  unsigned char sect;
      
  	if(flocker != 0xCC)return; // if not set, we got here unintentionally
    if(erase_write == 0)  { // in this mode word= no words in table
         // This number must be multiple of sector words (256)
      for (sect = 0; sect < wrd/256; sect++)  {
  	    Flash_Erase_Sector(addr + sect * 256);
      }
    } else  {
  	  Flash_Write_Word(addr, wrd);
    }
    return;
}

//*****************************************************************************
//* Function Name: Flash_Init
//* Description : Initialize Flash NVM for HCS12 by programming
//* FCLKDIV based on passed oscillator frequency, then
//* uprotect the array, and finally ensure PVIOL and
//* ACCERR are cleared by writing to them.
//*
//*****************************************************************************
void Flash_Init(unsigned long oscclk)  {
unsigned char fclk_val;
unsigned long temp;
  /* Next, initialize FCLKDIV register to ensure we can program/erase */
  temp = oscclk;
  if (oscclk >= 12000) {
    fclk_val = oscclk/8/200 - 1; /* FDIV8 set since above 12MHz clock */
    FCLKDIV = FCLKDIV | fclk_val | FDIV8;
  }
  else
  {
    fclk_val = oscclk/8/200 - 1;
    FCLKDIV = FCLKDIV | fclk_val;
  }
  FPROT = 0xFF; /* Disable all protection (only in special modes)*/
  FSTAT = FSTAT | (PVIOL|ACCERR);/* Clear any errors */
  return;
}
void Flash_Erase_Sector(unsigned int *address)  {

  FSTAT = (ACCERR | PVIOL); // clear errors
  (*address) = 0xFFFF;/* Dummy store to page to be erased */
  FCMD = ERASE;
  (void)DoOnStack(address);
  return;
}

void Flash_Write_Word(unsigned int *address, unsigned int data)  {
  FSTAT = (ACCERR | PVIOL); // clear errors
  (*address) = data; // Store desired data to address being programmed
  FCMD = PROG; // Store programming command in FCMD
  (void)DoOnStack(address); // just passed for PPAGE
  return;
}
